diff --git a/browser/base/content/browser-contentblocking.js b/browser/base/content/browser-contentblocking.js new file mode 100644 index 000000000000..feecd6419fe2 --- /dev/null +++ b/browser/base/content/browser-contentblocking.js @@ -0,0 +1,1490 @@ +/* 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/. */ + +ChromeUtils.defineModuleGetter( + this, + "ContentBlockingAllowList", + "resource://gre/modules/ContentBlockingAllowList.jsm" +); + +var Fingerprinting = { + PREF_ENABLED: "privacy.trackingprotection.fingerprinting.enabled", + reportBreakageLabel: "fingerprinting", + telemetryIdentifier: "fp", + + strings: { + get subViewBlocked() { + delete this.subViewBlocked; + return (this.subViewBlocked = gNavigatorBundle.getString( + "contentBlocking.fingerprintersView.blocked.label" + )); + }, + }, + + init() { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "enabled", + this.PREF_ENABLED, + false, + () => this.updateCategoryLabel() + ); + this.updateCategoryLabel(); + }, + + get categoryItem() { + delete this.categoryItem; + return (this.categoryItem = document.getElementById( + "identity-popup-content-blocking-category-fingerprinters" + )); + }, + + get categoryLabel() { + delete this.categoryLabel; + return (this.categoryLabel = document.getElementById( + "identity-popup-content-blocking-fingerprinters-state-label" + )); + }, + + get subViewList() { + delete this.subViewList; + return (this.subViewList = document.getElementById( + "identity-popup-fingerprintersView-list" + )); + }, + + updateCategoryLabel() { + let label; + if (this.enabled) { + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.fingerprinters.blocking.label" + : null; + } else { + label = ContentBlocking.showAllowedLabels + ? "contentBlocking.fingerprinters.allowed.label" + : null; + } + this.categoryLabel.textContent = label + ? gNavigatorBundle.getString(label) + : ""; + }, + + isBlocking(state) { + return ( + (state & + Ci.nsIWebProgressListener.STATE_BLOCKED_FINGERPRINTING_CONTENT) != + 0 + ); + }, + + isAllowing(state) { + return ( + this.enabled && + (state & Ci.nsIWebProgressListener.STATE_LOADED_FINGERPRINTING_CONTENT) != + 0 + ); + }, + + isDetected(state) { + return this.isBlocking(state) || this.isAllowing(state); + }, + + async updateSubView() { + let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); + contentBlockingLog = JSON.parse(contentBlockingLog); + + let fragment = document.createDocumentFragment(); + for (let [origin, actions] of Object.entries(contentBlockingLog)) { + let listItem = this._createListItem(origin, actions); + if (listItem) { + fragment.appendChild(listItem); + } + } + + this.subViewList.textContent = ""; + this.subViewList.append(fragment); + }, + + _createListItem(origin, actions) { + let isAllowed = actions.some(([state]) => this.isAllowing(state)); + let isDetected = + isAllowed || actions.some(([state]) => this.isBlocking(state)); + + if (!isDetected) { + return null; + } + + let uri = Services.io.newURI(origin); + + let listItem = document.createXULElement("hbox"); + listItem.className = "identity-popup-content-blocking-list-item"; + listItem.classList.toggle("allowed", isAllowed); + // Repeat the host in the tooltip in case it's too long + // and overflows in our panel. + listItem.tooltipText = uri.host; + + let image = document.createXULElement("image"); + image.className = "identity-popup-fingerprintersView-icon"; + image.classList.toggle("allowed", isAllowed); + listItem.append(image); + + let label = document.createXULElement("label"); + label.value = uri.host; + label.className = "identity-popup-content-blocking-list-host-label"; + label.setAttribute("crop", "end"); + listItem.append(label); + + if (!isAllowed) { + let stateLabel = document.createXULElement("label"); + stateLabel.value = this.strings.subViewBlocked; + stateLabel.className = "identity-popup-content-blocking-list-state-label"; + listItem.append(stateLabel); + } + + return listItem; + }, +}; + +var Cryptomining = { + PREF_ENABLED: "privacy.trackingprotection.cryptomining.enabled", + reportBreakageLabel: "cryptomining", + telemetryIdentifier: "cm", + + strings: { + get subViewBlocked() { + delete this.subViewBlocked; + return (this.subViewBlocked = gNavigatorBundle.getString( + "contentBlocking.cryptominersView.blocked.label" + )); + }, + }, + + init() { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "enabled", + this.PREF_ENABLED, + false, + () => this.updateCategoryLabel() + ); + this.updateCategoryLabel(); + }, + + get categoryItem() { + delete this.categoryItem; + return (this.categoryItem = document.getElementById( + "identity-popup-content-blocking-category-cryptominers" + )); + }, + + get categoryLabel() { + delete this.categoryLabel; + return (this.categoryLabel = document.getElementById( + "identity-popup-content-blocking-cryptominers-state-label" + )); + }, + + get subViewList() { + delete this.subViewList; + return (this.subViewList = document.getElementById( + "identity-popup-cryptominersView-list" + )); + }, + + updateCategoryLabel() { + let label; + if (this.enabled) { + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.cryptominers.blocking.label" + : null; + } else { + label = ContentBlocking.showAllowedLabels + ? "contentBlocking.cryptominers.allowed.label" + : null; + } + this.categoryLabel.textContent = label + ? gNavigatorBundle.getString(label) + : ""; + }, + + isBlocking(state) { + return ( + (state & Ci.nsIWebProgressListener.STATE_BLOCKED_CRYPTOMINING_CONTENT) != + 0 + ); + }, + + isAllowing(state) { + return ( + this.enabled && + (state & Ci.nsIWebProgressListener.STATE_LOADED_CRYPTOMINING_CONTENT) != 0 + ); + }, + + isDetected(state) { + return this.isBlocking(state) || this.isAllowing(state); + }, + + async updateSubView() { + let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); + contentBlockingLog = JSON.parse(contentBlockingLog); + + let fragment = document.createDocumentFragment(); + for (let [origin, actions] of Object.entries(contentBlockingLog)) { + let listItem = this._createListItem(origin, actions); + if (listItem) { + fragment.appendChild(listItem); + } + } + + this.subViewList.textContent = ""; + this.subViewList.append(fragment); + }, + + _createListItem(origin, actions) { + let isAllowed = actions.some(([state]) => this.isAllowing(state)); + let isDetected = + isAllowed || actions.some(([state]) => this.isBlocking(state)); + + if (!isDetected) { + return null; + } + + let uri = Services.io.newURI(origin); + + let listItem = document.createXULElement("hbox"); + listItem.className = "identity-popup-content-blocking-list-item"; + listItem.classList.toggle("allowed", isAllowed); + // Repeat the host in the tooltip in case it's too long + // and overflows in our panel. + listItem.tooltipText = uri.host; + + let image = document.createXULElement("image"); + image.className = "identity-popup-cryptominersView-icon"; + image.classList.toggle("allowed", isAllowed); + listItem.append(image); + + let label = document.createXULElement("label"); + label.value = uri.host; + label.className = "identity-popup-content-blocking-list-host-label"; + label.setAttribute("crop", "end"); + listItem.append(label); + + if (!isAllowed) { + let stateLabel = document.createXULElement("label"); + stateLabel.value = this.strings.subViewBlocked; + stateLabel.className = "identity-popup-content-blocking-list-state-label"; + listItem.append(stateLabel); + } + + return listItem; + }, +}; + +var TrackingProtection = { + reportBreakageLabel: "trackingprotection", + telemetryIdentifier: "tp", + PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled", + PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled", + PREF_TRACKING_TABLE: "urlclassifier.trackingTable", + PREF_TRACKING_ANNOTATION_TABLE: "urlclassifier.trackingAnnotationTable", + enabledGlobally: false, + enabledInPrivateWindows: false, + + get categoryItem() { + delete this.categoryItem; + return (this.categoryItem = document.getElementById( + "identity-popup-content-blocking-category-tracking-protection" + )); + }, + + get categoryLabel() { + delete this.categoryLabel; + return (this.categoryLabel = document.getElementById( + "identity-popup-content-blocking-tracking-protection-state-label" + )); + }, + + get subViewList() { + delete this.subViewList; + return (this.subViewList = document.getElementById( + "identity-popup-trackersView-list" + )); + }, + + get strictInfo() { + delete this.strictInfo; + return (this.strictInfo = document.getElementById( + "identity-popup-trackersView-strict-info" + )); + }, + + strings: { + get subViewBlocked() { + delete this.subViewBlocked; + return (this.subViewBlocked = gNavigatorBundle.getString( + "contentBlocking.trackersView.blocked.label" + )); + }, + }, + + init() { + this.updateEnabled(); + + Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this); + Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this); + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "trackingTable", + this.PREF_TRACKING_TABLE, + false + ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "trackingAnnotationTable", + this.PREF_TRACKING_ANNOTATION_TABLE, + 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 + ); + this.updateCategoryLabel(); + }, + + updateCategoryLabel() { + let label; + if (this.enabled) { + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.trackers.blocking.label" + : null; + } else { + label = ContentBlocking.showAllowedLabels + ? "contentBlocking.trackers.allowed.label" + : null; + } + this.categoryLabel.textContent = label + ? gNavigatorBundle.getString(label) + : ""; + }, + + isBlocking(state) { + return ( + (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) != 0 + ); + }, + + isAllowing(state) { + return ( + (state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) != 0 + ); + }, + + isDetected(state) { + return this.isBlocking(state) || this.isAllowing(state); + }, + + async updateSubView() { + let previousURI = gBrowser.currentURI.spec; + let previousWindow = gBrowser.selectedBrowser.innerWindowID; + + let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); + contentBlockingLog = JSON.parse(contentBlockingLog); + + // Don't tell the user to turn on TP if they are already blocking trackers. + this.strictInfo.hidden = this.enabled; + + let fragment = document.createDocumentFragment(); + for (let [origin, actions] of Object.entries(contentBlockingLog)) { + let listItem = await this._createListItem(origin, actions); + if (listItem) { + fragment.appendChild(listItem); + } + } + + // If we don't have trackers we would usually not show the menu item + // allowing the user to show the sub-panel. However, in the edge case + // that we annotated trackers on the page using the strict list but did + // not detect trackers on the page using the basic list, we currently + // still show the panel. To reduce the confusion, tell the user that we have + // not detected any tracker. + if (fragment.childNodes.length == 0) { + let emptyBox = document.createXULElement("vbox"); + let emptyImage = document.createXULElement("image"); + emptyImage.classList.add( + "identity-popup-content-blocking-trackersView-empty-image" + ); + emptyImage.classList.add("tracking-protection-icon"); + + let emptyLabel = document.createXULElement("label"); + emptyLabel.classList.add("identity-popup-content-blocking-empty-label"); + emptyLabel.textContent = gNavigatorBundle.getString( + "contentBlocking.trackersView.empty.label" + ); + + emptyBox.appendChild(emptyImage); + emptyBox.appendChild(emptyLabel); + fragment.appendChild(emptyBox); + + this.subViewList.classList.add("empty"); + } else { + this.subViewList.classList.remove("empty"); + } + + // This might have taken a while. Only update the list if we're still on the same page. + if ( + previousURI == gBrowser.currentURI.spec && + previousWindow == gBrowser.selectedBrowser.innerWindowID + ) { + this.subViewList.textContent = ""; + this.subViewList.append(fragment); + } + }, + + // Given a URI from a source that was tracking-annotated, figure out + // if it's really on the tracking table or just on the annotation table. + _isOnTrackingTable(uri) { + if (this.trackingTable == this.trackingAnnotationTable) { + return true; + } + + let feature = classifierService.getFeatureByName("tracking-protection"); + if (!feature) { + return false; + } + + return new Promise(resolve => { + classifierService.asyncClassifyLocalWithFeatures( + uri, + [feature], + Ci.nsIUrlClassifierFeature.blacklist, + list => resolve(!!list.length) + ); + }); + }, + + async _createListItem(origin, actions) { + // Figure out if this list entry was actually detected by TP or something else. + let isAllowed = actions.some(([state]) => this.isAllowing(state)); + let isDetected = + isAllowed || actions.some(([state]) => this.isBlocking(state)); + + if (!isDetected) { + return null; + } + + let uri = Services.io.newURI(origin); + + // Because we might use different lists for annotation vs. blocking, we + // need to make sure that this is a tracker that we would actually have blocked + // before showing it to the user. + let isTracker = await this._isOnTrackingTable(uri); + if (!isTracker) { + return null; + } + + let listItem = document.createXULElement("hbox"); + listItem.className = "identity-popup-content-blocking-list-item"; + listItem.classList.toggle("allowed", isAllowed); + // Repeat the host in the tooltip in case it's too long + // and overflows in our panel. + listItem.tooltipText = uri.host; + + let image = document.createXULElement("image"); + image.className = "identity-popup-trackersView-icon"; + image.classList.toggle("allowed", isAllowed); + listItem.append(image); + + let label = document.createXULElement("label"); + label.value = uri.host; + label.className = "identity-popup-content-blocking-list-host-label"; + label.setAttribute("crop", "end"); + listItem.append(label); + + if (!isAllowed) { + let stateLabel = document.createXULElement("label"); + stateLabel.value = this.strings.subViewBlocked; + stateLabel.className = "identity-popup-content-blocking-list-state-label"; + listItem.append(stateLabel); + } + + return listItem; + }, +}; + +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 + ], + + get categoryItem() { + delete this.categoryItem; + return (this.categoryItem = document.getElementById( + "identity-popup-content-blocking-category-cookies" + )); + }, + + get categoryLabel() { + delete this.categoryLabel; + return (this.categoryLabel = document.getElementById( + "identity-popup-content-blocking-cookies-state-label" + )); + }, + + get subViewList() { + delete this.subViewList; + return (this.subViewList = document.getElementById( + "identity-popup-cookiesView-list" + )); + }, + + strings: { + get subViewAllowed() { + delete this.subViewAllowed; + return (this.subViewAllowed = gNavigatorBundle.getString( + "contentBlocking.cookiesView.allowed.label" + )); + }, + + get subViewBlocked() { + delete this.subViewBlocked; + return (this.subViewBlocked = gNavigatorBundle.getString( + "contentBlocking.cookiesView.blocked.label" + )); + }, + }, + + 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"; + case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: + return "cookierestrictionsforeignpartitioned"; + } + }, + + updateCategoryLabel() { + let label; + switch (this.behaviorPref) { + case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.cookies.blocking3rdParty.label" + : null; + break; + case Ci.nsICookieService.BEHAVIOR_REJECT: + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.cookies.blockingAll.label" + : null; + break; + case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.cookies.blockingUnvisited.label" + : null; + break; + case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: + label = ContentBlocking.showBlockedLabels + ? "contentBlocking.cookies.blockingTrackers.label" + : null; + break; + default: + Cu.reportError( + `Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}` + ); + // fall through + case Ci.nsICookieService.BEHAVIOR_ACCEPT: + label = ContentBlocking.showAllowedLabels + ? "contentBlocking.cookies.allowed.label" + : null; + break; + } + this.categoryLabel.textContent = label + ? gNavigatorBundle.getString(label) + : ""; + }, + + init() { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "behaviorPref", + this.PREF_ENABLED, + Ci.nsICookieService.BEHAVIOR_ACCEPT, + this.updateCategoryLabel.bind(this) + ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "reportBreakageEnabled", + this.PREF_REPORT_BREAKAGE_ENABLED, + false + ); + this.updateCategoryLabel(); + }, + + get enabled() { + return this.PREF_ENABLED_VALUES.includes(this.behaviorPref); + }, + + isBlocking(state) { + return ( + (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) != 0 || + (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_ALL) != 0 || + (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_BY_PERMISSION) != + 0 || + (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_FOREIGN) != 0 + ); + }, + + isDetected(state) { + return (state & Ci.nsIWebProgressListener.STATE_COOKIES_LOADED) != 0; + }, + + async updateSubView() { + let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); + contentBlockingLog = JSON.parse(contentBlockingLog); + + let categories = this._processContentBlockingLog(contentBlockingLog); + + this.subViewList.textContent = ""; + + for (let category of ["firstParty", "trackers", "thirdParty"]) { + let box = document.createXULElement("vbox"); + let label = document.createXULElement("label"); + label.className = "identity-popup-cookiesView-list-header"; + label.textContent = gNavigatorBundle.getString( + `contentBlocking.cookiesView.${category}.label` + ); + box.appendChild(label); + + for (let info of categories[category]) { + box.appendChild(this._createListItem(info)); + } + + // If the category is empty, add a label noting that to the user. + if (categories[category].length == 0) { + let emptyLabel = document.createXULElement("label"); + emptyLabel.classList.add("identity-popup-content-blocking-empty-label"); + emptyLabel.textContent = gNavigatorBundle.getString( + `contentBlocking.cookiesView.${category}.empty.label` + ); + box.appendChild(emptyLabel); + } + + this.subViewList.appendChild(box); + } + }, + + _hasException(origin) { + for (let perm of Services.perms.getAllForPrincipal( + gBrowser.contentPrincipal + )) { + if ( + perm.type == "3rdPartyStorage^" + origin || + perm.type.startsWith("3rdPartyStorage^" + origin + "^") + ) { + return true; + } + } + + let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( + origin + ); + // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to + // make sure to include parent domains in the permission check for "cookies". + return ( + Services.perms.testPermissionFromPrincipal(principal, "cookie") != + Services.perms.UNKNOWN_ACTION + ); + }, + + _clearException(origin) { + for (let perm of Services.perms.getAllForPrincipal( + gBrowser.contentPrincipal + )) { + if ( + perm.type == "3rdPartyStorage^" + origin || + perm.type.startsWith("3rdPartyStorage^" + origin + "^") + ) { + Services.perms.removePermission(perm); + } + } + + // OAs don't matter here, so we can just use the hostname. + let host = Services.io.newURI(origin).host; + + // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to + // clear any cookie permissions from parent domains as well. + for (let perm of Services.perms.enumerator) { + if ( + perm.type == "cookie" && + Services.eTLD.hasRootDomain(host, perm.principal.URI.host) + ) { + Services.perms.removePermission(perm); + } + } + }, + + // Transforms and filters cookie entries in the content blocking log + // so that we can categorize and display them in the UI. + _processContentBlockingLog(log) { + let newLog = { + firstParty: [], + trackers: [], + thirdParty: [], + }; + + let firstPartyDomain = null; + try { + firstPartyDomain = Services.eTLD.getBaseDomain(gBrowser.currentURI); + } catch (e) { + // There are nasty edge cases here where someone is trying to set a cookie + // on a public suffix or an IP address. Just categorize those as third party... + if ( + e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS && + e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + ) { + throw e; + } + } + + for (let [origin, actions] of Object.entries(log)) { + if (!origin.startsWith("http")) { + continue; + } + + let info = { + origin, + isAllowed: true, + hasException: this._hasException(origin), + }; + let hasCookie = false; + let isTracker = false; + + // Extract information from the states entries in the content blocking log. + // Each state will contain a single state flag from nsIWebProgressListener. + // Note that we are using the same helper functions that are applied to the + // bit map passed to onSecurityChange (which contains multiple states), thus + // not checking exact equality, just presence of bits. + for (let [state, blocked] of actions) { + if (this.isDetected(state)) { + hasCookie = true; + } + if (TrackingProtection.isAllowing(state)) { + isTracker = true; + } + // blocked tells us whether the resource was actually blocked + // (which it may not be in case of an exception). + if (this.isBlocking(state)) { + info.isAllowed = !blocked; + } + } + + if (!hasCookie) { + continue; + } + + let isFirstParty = false; + try { + let uri = Services.io.newURI(origin); + isFirstParty = Services.eTLD.getBaseDomain(uri) == firstPartyDomain; + } catch (e) { + if ( + e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS && + e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + ) { + throw e; + } + } + + if (isFirstParty) { + newLog.firstParty.push(info); + } else if (isTracker) { + newLog.trackers.push(info); + } else { + newLog.thirdParty.push(info); + } + } + + return newLog; + }, + + _createListItem({ origin, isAllowed, hasException }) { + let listItem = document.createXULElement("hbox"); + listItem.className = "identity-popup-content-blocking-list-item"; + listItem.classList.toggle("allowed", isAllowed); + // Repeat the origin in the tooltip in case it's too long + // and overflows in our panel. + listItem.tooltipText = origin; + + let image = document.createXULElement("image"); + image.className = "identity-popup-cookiesView-icon"; + image.classList.toggle("allowed", isAllowed); + listItem.append(image); + + let label = document.createXULElement("label"); + label.value = origin; + label.className = "identity-popup-content-blocking-list-host-label"; + label.setAttribute("crop", "end"); + listItem.append(label); + + let stateLabel; + if (isAllowed && hasException) { + stateLabel = document.createXULElement("label"); + stateLabel.value = this.strings.subViewAllowed; + stateLabel.className = "identity-popup-content-blocking-list-state-label"; + listItem.append(stateLabel); + } else if (!isAllowed) { + stateLabel = document.createXULElement("label"); + stateLabel.value = this.strings.subViewBlocked; + stateLabel.className = "identity-popup-content-blocking-list-state-label"; + listItem.append(stateLabel); + } + + if (hasException) { + let removeException = document.createXULElement("button"); + removeException.className = "identity-popup-permission-remove-button"; + removeException.tooltipText = gNavigatorBundle.getFormattedString( + "contentBlocking.cookiesView.removeButton.tooltip", + [origin] + ); + removeException.addEventListener("click", () => { + this._clearException(origin); + // Just flip the display based on what state we had previously. + stateLabel.value = isAllowed + ? this.strings.subViewBlocked + : this.strings.subViewAllowed; + listItem.classList.toggle("allowed", !isAllowed); + image.classList.toggle("allowed", !isAllowed); + removeException.hidden = true; + }); + listItem.append(removeException); + } + + return listItem; + }, +}; + +var ContentBlocking = { + PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled", + PREF_REPORT_BREAKAGE_ENABLED: + "browser.contentblocking.reportBreakage.enabled", + PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url", + PREF_CB_CATEGORY: "browser.contentblocking.category", + PREF_SHOW_ALLOWED_LABELS: + "browser.contentblocking.control-center.ui.showAllowedLabels", + PREF_SHOW_BLOCKED_LABELS: + "browser.contentblocking.control-center.ui.showBlockedLabels", + + get content() { + delete this.content; + return (this.content = document.getElementById( + "identity-popup-content-blocking-content" + )); + }, + + get icon() { + delete this.icon; + return (this.icon = document.getElementById("tracking-protection-icon")); + }, + + get iconBox() { + delete this.iconBox; + return (this.iconBox = document.getElementById( + "tracking-protection-icon-box" + )); + }, + + get animatedIcon() { + delete this.animatedIcon; + return (this.animatedIcon = document.getElementById( + "tracking-protection-icon-animatable-image" + )); + }, + + get identityPopupMultiView() { + delete this.identityPopupMultiView; + return (this.identityPopupMultiView = document.getElementById( + "identity-popup-multiView" + )); + }, + + get reportBreakageButton() { + delete this.reportBreakageButton; + return (this.reportBreakageButton = document.getElementById( + "identity-popup-content-blocking-report-breakage" + )); + }, + + get reportBreakageURL() { + delete this.reportBreakageURL; + return (this.reportBreakageURL = document.getElementById( + "identity-popup-breakageReportView-collection-url" + )); + }, + + get reportBreakageLearnMore() { + delete this.reportBreakageLearnMore; + return (this.reportBreakageLearnMore = document.getElementById( + "identity-popup-breakageReportView-learn-more" + )); + }, + + get appMenuLabel() { + delete this.appMenuLabel; + return (this.appMenuLabel = document.getElementById("appMenu-tp-label")); + }, + + get identityPopup() { + delete this.identityPopup; + return (this.identityPopup = document.getElementById("identity-popup")); + }, + + get protectionsPopup() { + delete this.protectionsPopup; + return (this.protectionsPopup = document.getElementById( + "protections-popup" + )); + }, + + strings: { + get appMenuTitle() { + delete this.appMenuTitle; + return (this.appMenuTitle = gNavigatorBundle.getString( + "contentBlocking.title" + )); + }, + + get appMenuTooltip() { + delete this.appMenuTooltip; + if (AppConstants.platform == "win") { + return (this.appMenuTooltip = gNavigatorBundle.getString( + "contentBlocking.tooltipWin" + )); + } + return (this.appMenuTooltip = gNavigatorBundle.getString( + "contentBlocking.tooltipOther" + )); + }, + + get activeTooltipText() { + delete this.activeTooltipText; + return (this.activeTooltipText = gNavigatorBundle.getString( + "trackingProtection.icon.activeTooltip" + )); + }, + + get disabledTooltipText() { + delete this.disabledTooltipText; + return (this.disabledTooltipText = gNavigatorBundle.getString( + "trackingProtection.icon.disabledTooltip" + )); + }, + }, + + // 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. + // - isDetected(state): Given a content blocking state, whether the blocker has + // either allowed or blocked elements. + // - 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: [ + TrackingProtection, + ThirdPartyCookies, + Fingerprinting, + Cryptomining, + ], + + 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() { + this.animatedIcon.addEventListener("animationend", () => + this.iconBox.removeAttribute("animate") + ); + + let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); + this.reportBreakageLearnMore.href = baseURL + "blocking-breakage"; + + this.updateAnimationsEnabled = () => { + this.iconBox.toggleAttribute( + "animationsenabled", + Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false) + ); + }; + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "showBlockedLabels", + this.PREF_SHOW_BLOCKED_LABELS, + false, + () => { + for (let blocker of this.blockers) { + blocker.updateCategoryLabel(); + } + } + ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "showAllowedLabels", + this.PREF_SHOW_ALLOWED_LABELS, + false, + () => { + for (let blocker of this.blockers) { + blocker.updateCategoryLabel(); + } + } + ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "reportBreakageEnabled", + this.PREF_REPORT_BREAKAGE_ENABLED, + false + ); + + for (let blocker of this.blockers) { + if (blocker.init) { + blocker.init(); + } + } + + this.updateAnimationsEnabled(); + + Services.prefs.addObserver( + this.PREF_ANIMATIONS_ENABLED, + this.updateAnimationsEnabled + ); + + this.appMenuLabel.setAttribute("value", this.strings.appMenuTitle); + this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip); + + this.updateCBCategoryLabel = this.updateCBCategoryLabel.bind(this); + this.updateCBCategoryLabel(); + Services.prefs.addObserver( + this.PREF_CB_CATEGORY, + this.updateCBCategoryLabel + ); + }, + + 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_CB_CATEGORY, + this.updateCBCategoryLabel + ); + }, + + updateCBCategoryLabel() { + if (!Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY)) { + // Fallback to not setting a label, it's preferable to not set a label than to set an incorrect one. + return; + } + let button = document.getElementById( + "tracking-protection-preferences-button" + ); + let appMenuCategoryLabel = document.getElementById("appMenu-tp-category"); + let label; + let category = Services.prefs.getStringPref(this.PREF_CB_CATEGORY); + switch (category) { + case "standard": + label = gNavigatorBundle.getString("contentBlocking.category.standard"); + break; + case "strict": + label = gNavigatorBundle.getString("contentBlocking.category.strict"); + break; + case "custom": + label = gNavigatorBundle.getString("contentBlocking.category.custom"); + break; + } + appMenuCategoryLabel.value = label; + button.label = label; + }, + + hideIdentityPopupAndReload() { + this.identityPopup.hidePopup(); + BrowserReload(); + }, + + openPreferences(origin) { + openPreferences("privacy-trackingprotection", { origin }); + }, + + backToMainView() { + this.identityPopupMultiView.goBack(); + }, + + onSubmitBreakageReportClicked() { + this.identityPopup.hidePopup(); + + let comments = document.getElementById( + "identity-popup-breakageReportView-collection-comments" + ); + this.submitBreakageReport(this.reportURI, comments); + }, + + submitBreakageReport(uri, commentsTextarea) { + let reportEndpoint = Services.prefs.getStringPref( + this.PREF_REPORT_BREAKAGE_URL + ); + if (!reportEndpoint) { + return; + } + + let formData = new FormData(); + formData.set("title", uri.host); + + // Leave the ? at the end of the URL to signify that this URL had its query stripped. + let urlWithoutQuery = uri.asciiSpec.replace(uri.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 += `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_ENABLED}: ${Services.prefs.getIntPref( + ThirdPartyCookies.PREF_ENABLED + )}\n`; + body += `network.cookie.lifetimePolicy: ${Services.prefs.getIntPref( + "network.cookie.lifetimePolicy" + )}\n`; + body += `privacy.annotate_channels.strict_list.enabled: ${Services.prefs.getBoolPref( + "privacy.annotate_channels.strict_list.enabled" + )}\n`; + body += `privacy.restrict3rdpartystorage.expiration: ${Services.prefs.getIntPref( + "privacy.restrict3rdpartystorage.expiration" + )}\n`; + body += `${Fingerprinting.PREF_ENABLED}: ${Services.prefs.getBoolPref( + Fingerprinting.PREF_ENABLED + )}\n`; + body += `${Cryptomining.PREF_ENABLED}: ${Services.prefs.getBoolPref( + Cryptomining.PREF_ENABLED + )}\n`; + + body += "\n**Comments**\n" + commentsTextarea.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 + }` + ); + } else { + // Clear the textarea value when the report is submitted + commentsTextarea.value = ""; + } + }) + .catch(Cu.reportError); + }, + + toggleReportBreakageButton() { + // 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 && + !TrackingProtection.activated) + ) { + this.reportBreakageButton.removeAttribute("hidden"); + } else { + this.reportBreakageButton.setAttribute("hidden", "true"); + } + }, + + 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.value = urlWithoutQuery; + this.identityPopupMultiView.showSubView( + "identity-popup-breakageReportView" + ); + }, + + async showTrackersSubview() { + await TrackingProtection.updateSubView(); + this.identityPopupMultiView.showSubView("identity-popup-trackersView"); + }, + + async showCookiesSubview() { + await ThirdPartyCookies.updateSubView(); + this.identityPopupMultiView.showSubView("identity-popup-cookiesView"); + }, + + async showFingerprintersSubview() { + await Fingerprinting.updateSubView(); + this.identityPopupMultiView.showSubView( + "identity-popup-fingerprintersView" + ); + }, + + async showCryptominersSubview() { + await Cryptomining.updateSubView(); + this.identityPopupMultiView.showSubView("identity-popup-cryptominersView"); + }, + + shieldHistogramAdd(value) { + if (PrivateBrowsingUtils.isWindowPrivate(window)) { + return; + } + Services.telemetry + .getHistogramById("TRACKING_PROTECTION_SHIELD") + .add(value); + }, + + cryptominersHistogramAdd(value) { + Services.telemetry + .getHistogramById("CRYPTOMINERS_BLOCKED_COUNT") + .add(value); + }, + + fingerprintersHistogramAdd(value) { + Services.telemetry + .getHistogramById("FINGERPRINTERS_BLOCKED_COUNT") + .add(value); + }, + + // This triggers from top level location changes. + onLocationChange() { + // Reset blocking and exception status so that we can send telemetry + this.hadShieldState = false; + + // Don't deal with about:, file: etc. + if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) { + return; + } + + // Check whether the user has added an exception for this site. + let hasException = ContentBlockingAllowList.includes( + gBrowser.selectedBrowser + ); + + this.content.toggleAttribute("hasException", hasException); + this.protectionsPopup.toggleAttribute("hasException", hasException); + this.iconBox.toggleAttribute("hasException", hasException); + + // Add to telemetry per page load as a baseline measurement. + this.fingerprintersHistogramAdd("pageLoad"); + this.cryptominersHistogramAdd("pageLoad"); + this.shieldHistogramAdd(0); + }, + + onContentBlockingEvent(event, webProgress, isSimulated) { + let previousState = gBrowser.securityUI.contentBlockingEvent; + + // Don't deal with about:, file: etc. + if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) { + this.iconBox.removeAttribute("animate"); + this.iconBox.removeAttribute("active"); + this.iconBox.removeAttribute("hasException"); + return; + } + + let anyDetected = false; + let anyBlocking = 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.isBlocking(event); + blocker.categoryItem.classList.toggle("blocked", blocker.enabled); + let detected = blocker.isDetected(event); + blocker.categoryItem.hidden = !detected; + anyDetected = anyDetected || detected; + anyBlocking = anyBlocking || blocker.activated; + } + + // Check whether the user has added an exception for this site. + let hasException = ContentBlockingAllowList.includes( + gBrowser.selectedBrowser + ); + + // 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 + // allows us to play it from the start without choppiness next time. + if (isSimulated || !anyBlocking) { + this.iconBox.removeAttribute("animate"); + // Only play the animation when the shield is not already shown on the page (the visibility + // of the shield based on this onSecurityChange be determined afterwards). + } else if (anyBlocking && !this.iconBox.hasAttribute("active")) { + this.iconBox.setAttribute("animate", "true"); + } + + // 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. + for (let elt of [this.content, this.protectionsPopup]) { + elt.toggleAttribute("detected", anyDetected); + elt.toggleAttribute("blocking", anyBlocking); + elt.toggleAttribute("hasException", hasException); + } + + this.iconBox.toggleAttribute("active", anyBlocking); + this.iconBox.toggleAttribute("hasException", hasException); + + if (hasException) { + this.iconBox.setAttribute( + "tooltiptext", + this.strings.disabledTooltipText + ); + if (!this.hadShieldState && !isSimulated) { + this.hadShieldState = true; + this.shieldHistogramAdd(1); + } + } else if (anyBlocking) { + this.iconBox.setAttribute("tooltiptext", this.strings.activeTooltipText); + if (!this.hadShieldState && !isSimulated) { + this.hadShieldState = true; + this.shieldHistogramAdd(2); + } + } else { + this.iconBox.removeAttribute("tooltiptext"); + } + + // We report up to one instance of fingerprinting and cryptomining + // blocking and/or allowing per page load. + let fingerprintingBlocking = + Fingerprinting.isBlocking(event) && + !Fingerprinting.isBlocking(previousState); + let fingerprintingAllowing = + Fingerprinting.isAllowing(event) && + !Fingerprinting.isAllowing(previousState); + let cryptominingBlocking = + Cryptomining.isBlocking(event) && !Cryptomining.isBlocking(previousState); + let cryptominingAllowing = + Cryptomining.isAllowing(event) && !Cryptomining.isAllowing(previousState); + + if (fingerprintingBlocking) { + this.fingerprintersHistogramAdd("blocked"); + } else if (fingerprintingAllowing) { + this.fingerprintersHistogramAdd("allowed"); + } + + if (cryptominingBlocking) { + this.cryptominersHistogramAdd("blocked"); + } else if (cryptominingAllowing) { + this.cryptominersHistogramAdd("allowed"); + } + }, + + disableForCurrentPage() { + ContentBlockingAllowList.add(gBrowser.selectedBrowser); + + this.hideIdentityPopupAndReload(); + }, + + enableForCurrentPage() { + ContentBlockingAllowList.remove(gBrowser.selectedBrowser); + + this.hideIdentityPopupAndReload(); + }, +}; diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js index 18cf45a2fbe2..4572d290d8e4 100644 --- a/browser/base/content/browser-siteIdentity.js +++ b/browser/base/content/browser-siteIdentity.js @@ -343,10 +343,20 @@ var gIdentityHandler = { }, recordClick(object) { + let extra = {}; + for (let blocker of ContentBlocking.blockers) { + if (blocker.telemetryIdentifier) { + extra[blocker.telemetryIdentifier] = blocker.activated + ? "true" + : "false"; + } + } Services.telemetry.recordEvent( "security.ui.identitypopup", "click", - object + object, + null, + extra ); }, @@ -959,6 +969,8 @@ var gIdentityHandler = { // Update per-site permissions section. this.updateSitePermissions(); + + ContentBlocking.toggleReportBreakageButton(); }, setURI(uri) { @@ -1089,10 +1101,24 @@ var gIdentityHandler = { window.addEventListener("focus", this, true); } + let extra = {}; + for (let blocker of ContentBlocking.blockers) { + if (blocker.telemetryIdentifier) { + extra[blocker.telemetryIdentifier] = blocker.activated + ? "true" + : "false"; + } + } + + let shieldStatus = ContentBlocking.iconBox.hasAttribute("active") + ? "shield-showing" + : "shield-hidden"; Services.telemetry.recordEvent( "security.ui.identitypopup", "open", - "identity_popup" + "identity_popup", + shieldStatus, + extra ); }, diff --git a/browser/base/content/browser-siteProtections.js b/browser/base/content/browser-siteProtections.js index 103df16f4807..bdef5251039f 100644 --- a/browser/base/content/browser-siteProtections.js +++ b/browser/base/content/browser-siteProtections.js @@ -4,920 +4,10 @@ /* eslint-env mozilla/browser-window */ -ChromeUtils.defineModuleGetter( - this, - "ContentBlockingAllowList", - "resource://gre/modules/ContentBlockingAllowList.jsm" -); - -var Fingerprinting = { - PREF_ENABLED: "privacy.trackingprotection.fingerprinting.enabled", - reportBreakageLabel: "fingerprinting", - - strings: { - get subViewBlocked() { - delete this.subViewBlocked; - return (this.subViewBlocked = gNavigatorBundle.getString( - "contentBlocking.fingerprintersView.blocked.label" - )); - }, - }, - - init() { - XPCOMUtils.defineLazyPreferenceGetter( - this, - "enabled", - this.PREF_ENABLED, - false, - () => this.updateCategoryLabel() - ); - this.updateCategoryLabel(); - }, - - get categoryItem() { - delete this.categoryItem; - return (this.categoryItem = document.getElementById( - "identity-popup-content-blocking-category-fingerprinters" - )); - }, - - get categoryLabel() { - delete this.categoryLabel; - return (this.categoryLabel = document.getElementById( - "identity-popup-content-blocking-fingerprinters-state-label" - )); - }, - - get subViewList() { - delete this.subViewList; - return (this.subViewList = document.getElementById( - "identity-popup-fingerprintersView-list" - )); - }, - - updateCategoryLabel() { - let label; - if (this.enabled) { - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.fingerprinters.blocking.label" - : null; - } else { - label = gProtectionsHandler.showAllowedLabels - ? "contentBlocking.fingerprinters.allowed.label" - : null; - } - this.categoryLabel.textContent = label - ? gNavigatorBundle.getString(label) - : ""; - }, - - isBlocking(state) { - return ( - (state & - Ci.nsIWebProgressListener.STATE_BLOCKED_FINGERPRINTING_CONTENT) != - 0 - ); - }, - - isAllowing(state) { - return ( - this.enabled && - (state & Ci.nsIWebProgressListener.STATE_LOADED_FINGERPRINTING_CONTENT) != - 0 - ); - }, - - isDetected(state) { - return this.isBlocking(state) || this.isAllowing(state); - }, - - async updateSubView() { - let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); - contentBlockingLog = JSON.parse(contentBlockingLog); - - let fragment = document.createDocumentFragment(); - for (let [origin, actions] of Object.entries(contentBlockingLog)) { - let listItem = this._createListItem(origin, actions); - if (listItem) { - fragment.appendChild(listItem); - } - } - - this.subViewList.textContent = ""; - this.subViewList.append(fragment); - }, - - _createListItem(origin, actions) { - let isAllowed = actions.some(([state]) => this.isAllowing(state)); - let isDetected = - isAllowed || actions.some(([state]) => this.isBlocking(state)); - - if (!isDetected) { - return null; - } - - let uri = Services.io.newURI(origin); - - let listItem = document.createXULElement("hbox"); - listItem.className = "identity-popup-content-blocking-list-item"; - listItem.classList.toggle("allowed", isAllowed); - // Repeat the host in the tooltip in case it's too long - // and overflows in our panel. - listItem.tooltipText = uri.host; - - let image = document.createXULElement("image"); - image.className = "identity-popup-fingerprintersView-icon"; - image.classList.toggle("allowed", isAllowed); - listItem.append(image); - - let label = document.createXULElement("label"); - label.value = uri.host; - label.className = "identity-popup-content-blocking-list-host-label"; - label.setAttribute("crop", "end"); - listItem.append(label); - - if (!isAllowed) { - let stateLabel = document.createXULElement("label"); - stateLabel.value = this.strings.subViewBlocked; - stateLabel.className = "identity-popup-content-blocking-list-state-label"; - listItem.append(stateLabel); - } - - return listItem; - }, -}; - -var Cryptomining = { - PREF_ENABLED: "privacy.trackingprotection.cryptomining.enabled", - reportBreakageLabel: "cryptomining", - - strings: { - get subViewBlocked() { - delete this.subViewBlocked; - return (this.subViewBlocked = gNavigatorBundle.getString( - "contentBlocking.cryptominersView.blocked.label" - )); - }, - }, - - init() { - XPCOMUtils.defineLazyPreferenceGetter( - this, - "enabled", - this.PREF_ENABLED, - false, - () => this.updateCategoryLabel() - ); - this.updateCategoryLabel(); - }, - - get categoryItem() { - delete this.categoryItem; - return (this.categoryItem = document.getElementById( - "identity-popup-content-blocking-category-cryptominers" - )); - }, - - get categoryLabel() { - delete this.categoryLabel; - return (this.categoryLabel = document.getElementById( - "identity-popup-content-blocking-cryptominers-state-label" - )); - }, - - get subViewList() { - delete this.subViewList; - return (this.subViewList = document.getElementById( - "identity-popup-cryptominersView-list" - )); - }, - - updateCategoryLabel() { - let label; - if (this.enabled) { - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.cryptominers.blocking.label" - : null; - } else { - label = gProtectionsHandler.showAllowedLabels - ? "contentBlocking.cryptominers.allowed.label" - : null; - } - this.categoryLabel.textContent = label - ? gNavigatorBundle.getString(label) - : ""; - }, - - isBlocking(state) { - return ( - (state & Ci.nsIWebProgressListener.STATE_BLOCKED_CRYPTOMINING_CONTENT) != - 0 - ); - }, - - isAllowing(state) { - return ( - this.enabled && - (state & Ci.nsIWebProgressListener.STATE_LOADED_CRYPTOMINING_CONTENT) != 0 - ); - }, - - isDetected(state) { - return this.isBlocking(state) || this.isAllowing(state); - }, - - async updateSubView() { - let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); - contentBlockingLog = JSON.parse(contentBlockingLog); - - let fragment = document.createDocumentFragment(); - for (let [origin, actions] of Object.entries(contentBlockingLog)) { - let listItem = this._createListItem(origin, actions); - if (listItem) { - fragment.appendChild(listItem); - } - } - - this.subViewList.textContent = ""; - this.subViewList.append(fragment); - }, - - _createListItem(origin, actions) { - let isAllowed = actions.some(([state]) => this.isAllowing(state)); - let isDetected = - isAllowed || actions.some(([state]) => this.isBlocking(state)); - - if (!isDetected) { - return null; - } - - let uri = Services.io.newURI(origin); - - let listItem = document.createXULElement("hbox"); - listItem.className = "identity-popup-content-blocking-list-item"; - listItem.classList.toggle("allowed", isAllowed); - // Repeat the host in the tooltip in case it's too long - // and overflows in our panel. - listItem.tooltipText = uri.host; - - let image = document.createXULElement("image"); - image.className = "identity-popup-cryptominersView-icon"; - image.classList.toggle("allowed", isAllowed); - listItem.append(image); - - let label = document.createXULElement("label"); - label.value = uri.host; - label.className = "identity-popup-content-blocking-list-host-label"; - label.setAttribute("crop", "end"); - listItem.append(label); - - if (!isAllowed) { - let stateLabel = document.createXULElement("label"); - stateLabel.value = this.strings.subViewBlocked; - stateLabel.className = "identity-popup-content-blocking-list-state-label"; - listItem.append(stateLabel); - } - - return listItem; - }, -}; - -var TrackingProtection = { - reportBreakageLabel: "trackingprotection", - PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled", - PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled", - PREF_TRACKING_TABLE: "urlclassifier.trackingTable", - PREF_TRACKING_ANNOTATION_TABLE: "urlclassifier.trackingAnnotationTable", - enabledGlobally: false, - enabledInPrivateWindows: false, - - get categoryItem() { - delete this.categoryItem; - return (this.categoryItem = document.getElementById( - "identity-popup-content-blocking-category-tracking-protection" - )); - }, - - get categoryLabel() { - delete this.categoryLabel; - return (this.categoryLabel = document.getElementById( - "identity-popup-content-blocking-tracking-protection-state-label" - )); - }, - - get subViewList() { - delete this.subViewList; - return (this.subViewList = document.getElementById( - "identity-popup-trackersView-list" - )); - }, - - get strictInfo() { - delete this.strictInfo; - return (this.strictInfo = document.getElementById( - "identity-popup-trackersView-strict-info" - )); - }, - - strings: { - get subViewBlocked() { - delete this.subViewBlocked; - return (this.subViewBlocked = gNavigatorBundle.getString( - "contentBlocking.trackersView.blocked.label" - )); - }, - }, - - init() { - this.updateEnabled(); - - Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this); - Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this); - - XPCOMUtils.defineLazyPreferenceGetter( - this, - "trackingTable", - this.PREF_TRACKING_TABLE, - false - ); - XPCOMUtils.defineLazyPreferenceGetter( - this, - "trackingAnnotationTable", - this.PREF_TRACKING_ANNOTATION_TABLE, - 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 - ); - this.updateCategoryLabel(); - }, - - updateCategoryLabel() { - let label; - if (this.enabled) { - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.trackers.blocking.label" - : null; - } else { - label = gProtectionsHandler.showAllowedLabels - ? "contentBlocking.trackers.allowed.label" - : null; - } - this.categoryLabel.textContent = label - ? gNavigatorBundle.getString(label) - : ""; - }, - - isBlocking(state) { - return ( - (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) != 0 - ); - }, - - isAllowing(state) { - return ( - (state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) != 0 - ); - }, - - isDetected(state) { - return this.isBlocking(state) || this.isAllowing(state); - }, - - async updateSubView() { - let previousURI = gBrowser.currentURI.spec; - let previousWindow = gBrowser.selectedBrowser.innerWindowID; - - let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); - contentBlockingLog = JSON.parse(contentBlockingLog); - - // Don't tell the user to turn on TP if they are already blocking trackers. - this.strictInfo.hidden = this.enabled; - - let fragment = document.createDocumentFragment(); - for (let [origin, actions] of Object.entries(contentBlockingLog)) { - let listItem = await this._createListItem(origin, actions); - if (listItem) { - fragment.appendChild(listItem); - } - } - - // If we don't have trackers we would usually not show the menu item - // allowing the user to show the sub-panel. However, in the edge case - // that we annotated trackers on the page using the strict list but did - // not detect trackers on the page using the basic list, we currently - // still show the panel. To reduce the confusion, tell the user that we have - // not detected any tracker. - if (fragment.childNodes.length == 0) { - let emptyBox = document.createXULElement("vbox"); - let emptyImage = document.createXULElement("image"); - emptyImage.classList.add( - "identity-popup-content-blocking-trackersView-empty-image" - ); - emptyImage.classList.add("tracking-protection-icon"); - - let emptyLabel = document.createXULElement("label"); - emptyLabel.classList.add("identity-popup-content-blocking-empty-label"); - emptyLabel.textContent = gNavigatorBundle.getString( - "contentBlocking.trackersView.empty.label" - ); - - emptyBox.appendChild(emptyImage); - emptyBox.appendChild(emptyLabel); - fragment.appendChild(emptyBox); - - this.subViewList.classList.add("empty"); - } else { - this.subViewList.classList.remove("empty"); - } - - // This might have taken a while. Only update the list if we're still on the same page. - if ( - previousURI == gBrowser.currentURI.spec && - previousWindow == gBrowser.selectedBrowser.innerWindowID - ) { - this.subViewList.textContent = ""; - this.subViewList.append(fragment); - } - }, - - // Given a URI from a source that was tracking-annotated, figure out - // if it's really on the tracking table or just on the annotation table. - _isOnTrackingTable(uri) { - if (this.trackingTable == this.trackingAnnotationTable) { - return true; - } - - let feature = classifierService.getFeatureByName("tracking-protection"); - if (!feature) { - return false; - } - - return new Promise(resolve => { - classifierService.asyncClassifyLocalWithFeatures( - uri, - [feature], - Ci.nsIUrlClassifierFeature.blacklist, - list => resolve(!!list.length) - ); - }); - }, - - async _createListItem(origin, actions) { - // Figure out if this list entry was actually detected by TP or something else. - let isAllowed = actions.some(([state]) => this.isAllowing(state)); - let isDetected = - isAllowed || actions.some(([state]) => this.isBlocking(state)); - - if (!isDetected) { - return null; - } - - let uri = Services.io.newURI(origin); - - // Because we might use different lists for annotation vs. blocking, we - // need to make sure that this is a tracker that we would actually have blocked - // before showing it to the user. - let isTracker = await this._isOnTrackingTable(uri); - if (!isTracker) { - return null; - } - - let listItem = document.createXULElement("hbox"); - listItem.className = "identity-popup-content-blocking-list-item"; - listItem.classList.toggle("allowed", isAllowed); - // Repeat the host in the tooltip in case it's too long - // and overflows in our panel. - listItem.tooltipText = uri.host; - - let image = document.createXULElement("image"); - image.className = "identity-popup-trackersView-icon"; - image.classList.toggle("allowed", isAllowed); - listItem.append(image); - - let label = document.createXULElement("label"); - label.value = uri.host; - label.className = "identity-popup-content-blocking-list-host-label"; - label.setAttribute("crop", "end"); - listItem.append(label); - - if (!isAllowed) { - let stateLabel = document.createXULElement("label"); - stateLabel.value = this.strings.subViewBlocked; - stateLabel.className = "identity-popup-content-blocking-list-state-label"; - listItem.append(stateLabel); - } - - return listItem; - }, -}; - -var ThirdPartyCookies = { - 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 - ], - - get categoryItem() { - delete this.categoryItem; - return (this.categoryItem = document.getElementById( - "identity-popup-content-blocking-category-cookies" - )); - }, - - get categoryLabel() { - delete this.categoryLabel; - return (this.categoryLabel = document.getElementById( - "identity-popup-content-blocking-cookies-state-label" - )); - }, - - get subViewList() { - delete this.subViewList; - return (this.subViewList = document.getElementById( - "identity-popup-cookiesView-list" - )); - }, - - strings: { - get subViewAllowed() { - delete this.subViewAllowed; - return (this.subViewAllowed = gNavigatorBundle.getString( - "contentBlocking.cookiesView.allowed.label" - )); - }, - - get subViewBlocked() { - delete this.subViewBlocked; - return (this.subViewBlocked = gNavigatorBundle.getString( - "contentBlocking.cookiesView.blocked.label" - )); - }, - }, - - 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"; - case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: - return "cookierestrictionsforeignpartitioned"; - } - }, - - updateCategoryLabel() { - let label; - switch (this.behaviorPref) { - case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.cookies.blocking3rdParty.label" - : null; - break; - case Ci.nsICookieService.BEHAVIOR_REJECT: - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.cookies.blockingAll.label" - : null; - break; - case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.cookies.blockingUnvisited.label" - : null; - break; - case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: - label = gProtectionsHandler.showBlockedLabels - ? "contentBlocking.cookies.blockingTrackers.label" - : null; - break; - default: - Cu.reportError( - `Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}` - ); - // fall through - case Ci.nsICookieService.BEHAVIOR_ACCEPT: - label = gProtectionsHandler.showAllowedLabels - ? "contentBlocking.cookies.allowed.label" - : null; - break; - } - this.categoryLabel.textContent = label - ? gNavigatorBundle.getString(label) - : ""; - }, - - init() { - XPCOMUtils.defineLazyPreferenceGetter( - this, - "behaviorPref", - this.PREF_ENABLED, - Ci.nsICookieService.BEHAVIOR_ACCEPT, - this.updateCategoryLabel.bind(this) - ); - XPCOMUtils.defineLazyPreferenceGetter( - this, - "reportBreakageEnabled", - this.PREF_REPORT_BREAKAGE_ENABLED, - false - ); - this.updateCategoryLabel(); - }, - - get enabled() { - return this.PREF_ENABLED_VALUES.includes(this.behaviorPref); - }, - - isBlocking(state) { - return ( - (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) != 0 || - (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_ALL) != 0 || - (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_BY_PERMISSION) != - 0 || - (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_FOREIGN) != 0 - ); - }, - - isDetected(state) { - return (state & Ci.nsIWebProgressListener.STATE_COOKIES_LOADED) != 0; - }, - - async updateSubView() { - let contentBlockingLog = await gBrowser.selectedBrowser.getContentBlockingLog(); - contentBlockingLog = JSON.parse(contentBlockingLog); - - let categories = this._processContentBlockingLog(contentBlockingLog); - - this.subViewList.textContent = ""; - - for (let category of ["firstParty", "trackers", "thirdParty"]) { - let box = document.createXULElement("vbox"); - let label = document.createXULElement("label"); - label.className = "identity-popup-cookiesView-list-header"; - label.textContent = gNavigatorBundle.getString( - `contentBlocking.cookiesView.${category}.label` - ); - box.appendChild(label); - - for (let info of categories[category]) { - box.appendChild(this._createListItem(info)); - } - - // If the category is empty, add a label noting that to the user. - if (categories[category].length == 0) { - let emptyLabel = document.createXULElement("label"); - emptyLabel.classList.add("identity-popup-content-blocking-empty-label"); - emptyLabel.textContent = gNavigatorBundle.getString( - `contentBlocking.cookiesView.${category}.empty.label` - ); - box.appendChild(emptyLabel); - } - - this.subViewList.appendChild(box); - } - }, - - _hasException(origin) { - for (let perm of Services.perms.getAllForPrincipal( - gBrowser.contentPrincipal - )) { - if ( - perm.type == "3rdPartyStorage^" + origin || - perm.type.startsWith("3rdPartyStorage^" + origin + "^") - ) { - return true; - } - } - - let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( - origin - ); - // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to - // make sure to include parent domains in the permission check for "cookies". - return ( - Services.perms.testPermissionFromPrincipal(principal, "cookie") != - Services.perms.UNKNOWN_ACTION - ); - }, - - _clearException(origin) { - for (let perm of Services.perms.getAllForPrincipal( - gBrowser.contentPrincipal - )) { - if ( - perm.type == "3rdPartyStorage^" + origin || - perm.type.startsWith("3rdPartyStorage^" + origin + "^") - ) { - Services.perms.removePermission(perm); - } - } - - // OAs don't matter here, so we can just use the hostname. - let host = Services.io.newURI(origin).host; - - // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to - // clear any cookie permissions from parent domains as well. - for (let perm of Services.perms.enumerator) { - if ( - perm.type == "cookie" && - Services.eTLD.hasRootDomain(host, perm.principal.URI.host) - ) { - Services.perms.removePermission(perm); - } - } - }, - - // Transforms and filters cookie entries in the content blocking log - // so that we can categorize and display them in the UI. - _processContentBlockingLog(log) { - let newLog = { - firstParty: [], - trackers: [], - thirdParty: [], - }; - - let firstPartyDomain = null; - try { - firstPartyDomain = Services.eTLD.getBaseDomain(gBrowser.currentURI); - } catch (e) { - // There are nasty edge cases here where someone is trying to set a cookie - // on a public suffix or an IP address. Just categorize those as third party... - if ( - e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS && - e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS - ) { - throw e; - } - } - - for (let [origin, actions] of Object.entries(log)) { - if (!origin.startsWith("http")) { - continue; - } - - let info = { - origin, - isAllowed: true, - hasException: this._hasException(origin), - }; - let hasCookie = false; - let isTracker = false; - - // Extract information from the states entries in the content blocking log. - // Each state will contain a single state flag from nsIWebProgressListener. - // Note that we are using the same helper functions that are applied to the - // bit map passed to onSecurityChange (which contains multiple states), thus - // not checking exact equality, just presence of bits. - for (let [state, blocked] of actions) { - if (this.isDetected(state)) { - hasCookie = true; - } - if (TrackingProtection.isAllowing(state)) { - isTracker = true; - } - // blocked tells us whether the resource was actually blocked - // (which it may not be in case of an exception). - if (this.isBlocking(state)) { - info.isAllowed = !blocked; - } - } - - if (!hasCookie) { - continue; - } - - let isFirstParty = false; - try { - let uri = Services.io.newURI(origin); - isFirstParty = Services.eTLD.getBaseDomain(uri) == firstPartyDomain; - } catch (e) { - if ( - e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS && - e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS - ) { - throw e; - } - } - - if (isFirstParty) { - newLog.firstParty.push(info); - } else if (isTracker) { - newLog.trackers.push(info); - } else { - newLog.thirdParty.push(info); - } - } - - return newLog; - }, - - _createListItem({ origin, isAllowed, hasException }) { - let listItem = document.createXULElement("hbox"); - listItem.className = "identity-popup-content-blocking-list-item"; - listItem.classList.toggle("allowed", isAllowed); - // Repeat the origin in the tooltip in case it's too long - // and overflows in our panel. - listItem.tooltipText = origin; - - let image = document.createXULElement("image"); - image.className = "identity-popup-cookiesView-icon"; - image.classList.toggle("allowed", isAllowed); - listItem.append(image); - - let label = document.createXULElement("label"); - label.value = origin; - label.className = "identity-popup-content-blocking-list-host-label"; - label.setAttribute("crop", "end"); - listItem.append(label); - - let stateLabel; - if (isAllowed && hasException) { - stateLabel = document.createXULElement("label"); - stateLabel.value = this.strings.subViewAllowed; - stateLabel.className = "identity-popup-content-blocking-list-state-label"; - listItem.append(stateLabel); - } else if (!isAllowed) { - stateLabel = document.createXULElement("label"); - stateLabel.value = this.strings.subViewBlocked; - stateLabel.className = "identity-popup-content-blocking-list-state-label"; - listItem.append(stateLabel); - } - - if (hasException) { - let removeException = document.createXULElement("button"); - removeException.className = "identity-popup-permission-remove-button"; - removeException.tooltipText = gNavigatorBundle.getFormattedString( - "contentBlocking.cookiesView.removeButton.tooltip", - [origin] - ); - removeException.addEventListener("click", () => { - this._clearException(origin); - // Just flip the display based on what state we had previously. - stateLabel.value = isAllowed - ? this.strings.subViewBlocked - : this.strings.subViewAllowed; - listItem.classList.toggle("allowed", !isAllowed); - image.classList.toggle("allowed", !isAllowed); - removeException.hidden = true; - }); - listItem.append(removeException); - } - - return listItem; - }, -}; - /** * Utility object to handle manipulations of the protections indicators in the UI */ var gProtectionsHandler = { - PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled", - PREF_REPORT_BREAKAGE_ENABLED: - "browser.contentblocking.reportBreakage.enabled", - PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url", - PREF_CB_CATEGORY: "browser.contentblocking.category", - PREF_SHOW_ALLOWED_LABELS: - "browser.contentblocking.control-center.ui.showAllowedLabels", - PREF_SHOW_BLOCKED_LABELS: - "browser.contentblocking.control-center.ui.showBlockedLabels", - // smart getters get _protectionsPopup() { delete this._protectionsPopup; @@ -925,22 +15,6 @@ var gProtectionsHandler = { "protections-popup" )); }, - get iconBox() { - delete this.iconBox; - return (this.iconBox = document.getElementById( - "tracking-protection-icon-box" - )); - }, - get animatedIcon() { - delete this.animatedIcon; - return (this.animatedIcon = document.getElementById( - "tracking-protection-icon-animatable-image" - )); - }, - get appMenuLabel() { - delete this.appMenuLabel; - return (this.appMenuLabel = document.getElementById("appMenu-tp-label")); - }, get _protectionsIconBox() { delete this._protectionsIconBox; return (this._protectionsIconBox = document.getElementById( @@ -977,12 +51,6 @@ var gProtectionsHandler = { "protections-popup-tp-switch" )); }, - get _breakageLink() { - delete this._breakageLink; - return (this._breakageLink = document.getElementById( - "protections-popup-tp-switch-breakage-link" - )); - }, get _protectionPopupSettingsButton() { delete this._protectionPopupSettingsButton; return (this._protectionPopupSettingsButton = document.getElementById( @@ -1019,224 +87,15 @@ var gProtectionsHandler = { "protections-popup-sendReportView-collection-url" )); }, - - strings: { - get appMenuTitle() { - delete this.appMenuTitle; - return (this.appMenuTitle = gNavigatorBundle.getString( - "contentBlocking.title" - )); - }, - - get appMenuTooltip() { - delete this.appMenuTooltip; - if (AppConstants.platform == "win") { - return (this.appMenuTooltip = gNavigatorBundle.getString( - "contentBlocking.tooltipWin" - )); - } - return (this.appMenuTooltip = gNavigatorBundle.getString( - "contentBlocking.tooltipOther" - )); - }, - - get activeTooltipText() { - delete this.activeTooltipText; - return (this.activeTooltipText = gNavigatorBundle.getString( - "trackingProtection.icon.activeTooltip" - )); - }, - - get disabledTooltipText() { - delete this.disabledTooltipText; - return (this.disabledTooltipText = gNavigatorBundle.getString( - "trackingProtection.icon.disabledTooltip" - )); - }, - }, - - // 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. - // - isDetected(state): Given a content blocking state, whether the blocker has - // either allowed or blocked elements. - // - 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 gProtectionsHandler.init() and gProtectionsHandler.uninit(). - blockers: [ - TrackingProtection, - ThirdPartyCookies, - Fingerprinting, - Cryptomining, - ], - - init() { - this.animatedIcon.addEventListener("animationend", () => - this.iconBox.removeAttribute("animate") - ); - - this.updateAnimationsEnabled = () => { - this.iconBox.toggleAttribute( - "animationsenabled", - Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false) - ); - }; - + get _protectionsPopupToastTimeout() { + delete this._protectionsPopupToastTimeout; XPCOMUtils.defineLazyPreferenceGetter( this, "_protectionsPopupToastTimeout", "browser.protections_panel.toast.timeout", 5000 ); - XPCOMUtils.defineLazyPreferenceGetter( - this, - "showBlockedLabels", - this.PREF_SHOW_BLOCKED_LABELS, - false, - () => { - for (let blocker of this.blockers) { - blocker.updateCategoryLabel(); - } - } - ); - XPCOMUtils.defineLazyPreferenceGetter( - this, - "showAllowedLabels", - this.PREF_SHOW_ALLOWED_LABELS, - false, - () => { - for (let blocker of this.blockers) { - blocker.updateCategoryLabel(); - } - } - ); - XPCOMUtils.defineLazyPreferenceGetter( - this, - "reportBreakageEnabled", - this.PREF_REPORT_BREAKAGE_ENABLED, - false - ); - - for (let blocker of this.blockers) { - if (blocker.init) { - blocker.init(); - } - } - - this.updateAnimationsEnabled(); - - Services.prefs.addObserver( - this.PREF_ANIMATIONS_ENABLED, - this.updateAnimationsEnabled - ); - - let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); - gProtectionsHandler._protectionsPopupSendReportLearnMore.href = - baseURL + "blocking-breakage"; - - this.appMenuLabel.setAttribute("value", this.strings.appMenuTitle); - this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip); - - this.updateCBCategoryLabel = this.updateCBCategoryLabel.bind(this); - this.updateCBCategoryLabel(); - Services.prefs.addObserver( - this.PREF_CB_CATEGORY, - this.updateCBCategoryLabel - ); - }, - - 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_CB_CATEGORY, - this.updateCBCategoryLabel - ); - }, - - updateCBCategoryLabel() { - if (!Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY)) { - // Fallback to not setting a label, it's preferable to not set a label than to set an incorrect one. - return; - } - let button = document.getElementById( - "tracking-protection-preferences-button" - ); - let appMenuCategoryLabel = document.getElementById("appMenu-tp-category"); - let label; - let category = Services.prefs.getStringPref(this.PREF_CB_CATEGORY); - switch (category) { - case "standard": - label = gNavigatorBundle.getString("contentBlocking.category.standard"); - break; - case "strict": - label = gNavigatorBundle.getString("contentBlocking.category.strict"); - break; - case "custom": - label = gNavigatorBundle.getString("contentBlocking.category.custom"); - break; - } - appMenuCategoryLabel.value = label; - button.label = label; - }, - - openPreferences(origin) { - openPreferences("privacy-trackingprotection", { origin }); - }, - - async showTrackersSubview() { - await TrackingProtection.updateSubView(); - this._protectionsPopupMultiView.showSubView("identity-popup-trackersView"); - }, - - async showCookiesSubview() { - await ThirdPartyCookies.updateSubView(); - this._protectionsPopupMultiView.showSubView("identity-popup-cookiesView"); - }, - - async showFingerprintersSubview() { - await Fingerprinting.updateSubView(); - this._protectionsPopupMultiView.showSubView( - "identity-popup-fingerprintersView" - ); - }, - - async showCryptominersSubview() { - await Cryptomining.updateSubView(); - this._protectionsPopupMultiView.showSubView( - "identity-popup-cryptominersView" - ); - }, - - shieldHistogramAdd(value) { - if (PrivateBrowsingUtils.isWindowPrivate(window)) { - return; - } - Services.telemetry - .getHistogramById("TRACKING_PROTECTION_SHIELD") - .add(value); - }, - - cryptominersHistogramAdd(value) { - Services.telemetry - .getHistogramById("CRYPTOMINERS_BLOCKED_COUNT") - .add(value); - }, - - fingerprintersHistogramAdd(value) { - Services.telemetry - .getHistogramById("FINGERPRINTERS_BLOCKED_COUNT") - .add(value); + return this._protectionsPopupToastTimeout; }, handleProtectionsButtonEvent(event) { @@ -1277,142 +136,26 @@ var gProtectionsHandler = { } }, - // This triggers from top level location changes. onLocationChange() { - if (this._showToastAfterRefresh) { - this._showToastAfterRefresh = false; - - // We only display the toast if we're still on the same page. - if ( - this._previousURI == gBrowser.currentURI.spec && - this._previousOuterWindowID == gBrowser.selectedBrowser.outerWindowID - ) { - this.showProtectionsPopup({ - toast: true, - }); - } - } - - // Reset blocking and exception status so that we can send telemetry - this.hadShieldState = false; - - // Don't deal with about:, file: etc. - if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) { + if (!this._showToastAfterRefresh) { return; } - // Check whether the user has added an exception for this site. - let hasException = ContentBlockingAllowList.includes( - gBrowser.selectedBrowser - ); + this._showToastAfterRefresh = false; - this._protectionsPopup.toggleAttribute("hasException", hasException); - this.iconBox.toggleAttribute("hasException", hasException); - - // Add to telemetry per page load as a baseline measurement. - this.fingerprintersHistogramAdd("pageLoad"); - this.cryptominersHistogramAdd("pageLoad"); - this.shieldHistogramAdd(0); - }, - - onContentBlockingEvent(event, webProgress, isSimulated) { - let previousState = gBrowser.securityUI.contentBlockingEvent; - - // Don't deal with about:, file: etc. - if (!ContentBlockingAllowList.canHandle(gBrowser.selectedBrowser)) { - this.iconBox.removeAttribute("animate"); - this.iconBox.removeAttribute("active"); - this.iconBox.removeAttribute("hasException"); + // We only display the toast if we're still on the same page. + if ( + this._previousURI != gBrowser.currentURI.spec || + this._previousOuterWindowID != gBrowser.selectedBrowser.outerWindowID + ) { return; } - let anyDetected = false; - let anyBlocking = 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.isBlocking(event); - blocker.categoryItem.classList.toggle("blocked", blocker.enabled); - let detected = blocker.isDetected(event); - blocker.categoryItem.hidden = !detected; - anyDetected = anyDetected || detected; - anyBlocking = anyBlocking || blocker.activated; - } - - // Check whether the user has added an exception for this site. - let hasException = ContentBlockingAllowList.includes( - gBrowser.selectedBrowser - ); - - // 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 - // allows us to play it from the start without choppiness next time. - if (isSimulated || !anyBlocking) { - this.iconBox.removeAttribute("animate"); - // Only play the animation when the shield is not already shown on the page (the visibility - // of the shield based on this onSecurityChange be determined afterwards). - } else if (anyBlocking && !this.iconBox.hasAttribute("active")) { - this.iconBox.setAttribute("animate", "true"); - } - - // 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. - this._protectionsPopup.toggleAttribute("detected", anyDetected); - this._protectionsPopup.toggleAttribute("blocking", anyBlocking); - this._protectionsPopup.toggleAttribute("hasException", hasException); - - this.iconBox.toggleAttribute("active", anyBlocking); - this.iconBox.toggleAttribute("hasException", hasException); - - if (hasException) { - this.iconBox.setAttribute( - "tooltiptext", - this.strings.disabledTooltipText - ); - if (!this.hadShieldState && !isSimulated) { - this.hadShieldState = true; - this.shieldHistogramAdd(1); - } - } else if (anyBlocking) { - this.iconBox.setAttribute("tooltiptext", this.strings.activeTooltipText); - if (!this.hadShieldState && !isSimulated) { - this.hadShieldState = true; - this.shieldHistogramAdd(2); - } - } else { - this.iconBox.removeAttribute("tooltiptext"); - } - - // We report up to one instance of fingerprinting and cryptomining - // blocking and/or allowing per page load. - let fingerprintingBlocking = - Fingerprinting.isBlocking(event) && - !Fingerprinting.isBlocking(previousState); - let fingerprintingAllowing = - Fingerprinting.isAllowing(event) && - !Fingerprinting.isAllowing(previousState); - let cryptominingBlocking = - Cryptomining.isBlocking(event) && !Cryptomining.isBlocking(previousState); - let cryptominingAllowing = - Cryptomining.isAllowing(event) && !Cryptomining.isAllowing(previousState); - - if (fingerprintingBlocking) { - this.fingerprintersHistogramAdd("blocked"); - } else if (fingerprintingAllowing) { - this.fingerprintersHistogramAdd("allowed"); - } - - if (cryptominingBlocking) { - this.cryptominersHistogramAdd("blocked"); - } else if (cryptominingAllowing) { - this.cryptominersHistogramAdd("allowed"); - } + this.showProtectionsPopup({ + toast: true, + }); }, + handleEvent(event) { let elem = document.activeElement; let position = elem.compareDocumentPosition(this._protectionsPopup); @@ -1460,18 +203,6 @@ var gProtectionsHandler = { this.setTrackersBlockedCounter(244051); }, - disableForCurrentPage() { - ContentBlockingAllowList.add(gBrowser.selectedBrowser); - PanelMultiView.hidePopup(this._protectionsPopup); - BrowserReload(); - }, - - enableForCurrentPage() { - ContentBlockingAllowList.remove(gBrowser.selectedBrowser); - PanelMultiView.hidePopup(this._protectionsPopup); - BrowserReload(); - }, - async onTPSwitchCommand(event) { // When the switch is clicked, we wait 500ms and then disable/enable // protections, causing the page to refresh, and close the popup. @@ -1505,11 +236,14 @@ var gProtectionsHandler = { await new Promise(resolve => setTimeout(resolve, 500)); if (newExceptionState) { - this.disableForCurrentPage(); + ContentBlocking.disableForCurrentPage(); + gIdentityHandler.recordClick("unblock"); } else { - this.enableForCurrentPage(); + ContentBlocking.enableForCurrentPage(); + gIdentityHandler.recordClick("block"); } + PanelMultiView.hidePopup(this._protectionsPopup); delete this._TPSwitchCommanding; }, @@ -1565,10 +299,6 @@ var gProtectionsHandler = { ); } - // Adjust "site not working?" visibility based on whether we're - // blocking something or not. - gProtectionsHandler.toggleBreakageLink(); - // Now open the popup, anchored off the primary chrome element PanelMultiView.openPopup( this._protectionsPopup, @@ -1600,117 +330,15 @@ var gProtectionsHandler = { ); }, - toggleBreakageLink() { - // 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 && - !TrackingProtection.activated) - ) { - this._breakageLink.removeAttribute("hidden"); - } else { - this._breakageLink.setAttribute("hidden", "true"); - } - }, - - submitBreakageReport(uri) { - let reportEndpoint = Services.prefs.getStringPref( - this.PREF_REPORT_BREAKAGE_URL - ); - if (!reportEndpoint) { - return; - } - - let commentsTextarea = document.getElementById( - "protections-popup-sendReportView-collection-comments" - ); - - let formData = new FormData(); - formData.set("title", uri.host); - - // Leave the ? at the end of the URL to signify that this URL had its query stripped. - let urlWithoutQuery = uri.asciiSpec.replace(uri.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 += `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_ENABLED}: ${Services.prefs.getIntPref( - ThirdPartyCookies.PREF_ENABLED - )}\n`; - body += `network.cookie.lifetimePolicy: ${Services.prefs.getIntPref( - "network.cookie.lifetimePolicy" - )}\n`; - body += `privacy.annotate_channels.strict_list.enabled: ${Services.prefs.getBoolPref( - "privacy.annotate_channels.strict_list.enabled" - )}\n`; - body += `privacy.restrict3rdpartystorage.expiration: ${Services.prefs.getIntPref( - "privacy.restrict3rdpartystorage.expiration" - )}\n`; - body += `${Fingerprinting.PREF_ENABLED}: ${Services.prefs.getBoolPref( - Fingerprinting.PREF_ENABLED - )}\n`; - body += `${Cryptomining.PREF_ENABLED}: ${Services.prefs.getBoolPref( - Cryptomining.PREF_ENABLED - )}\n`; - - body += "\n**Comments**\n" + commentsTextarea.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 - }` - ); - } else { - // Clear the textarea value when the report is submitted - commentsTextarea.value = ""; - } - }) - .catch(Cu.reportError); - }, - onSendReportClicked() { this._protectionsPopup.hidePopup(); - this.submitBreakageReport(this.reportURI); + let comments = document.getElementById( + "protections-popup-sendReportView-collection-comments" + ); + ContentBlocking.submitBreakageReport(this.reportURI, comments); }, }; + +let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); +gProtectionsHandler._protectionsPopupSendReportLearnMore.href = + baseURL + "blocking-breakage"; diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index fd76b329fa47..2ca91f663e1e 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1943,7 +1943,7 @@ var gBrowserInit = { BookmarkingUI.init(); BrowserSearch.delayedStartupInit(); AutoShowBookmarksToolbar.init(); - gProtectionsHandler.init(); + ContentBlocking.init(); let safeMode = document.getElementById("helpSafeMode"); if (Services.appinfo.inSafeMode) { @@ -2423,7 +2423,7 @@ var gBrowserInit = { Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab); ctrlTab.uninit(); gBrowserThumbnails.uninit(); - gProtectionsHandler.uninit(); + ContentBlocking.uninit(); FullZoom.destroy(); Services.obs.removeObserver(gIdentityHandler, "perm-changed"); @@ -5739,7 +5739,7 @@ var XULBrowserWindow = { ); } - gProtectionsHandler.onContentBlockingEvent( + ContentBlocking.onContentBlockingEvent( this._event, aWebProgress, aIsSimulated @@ -6366,6 +6366,8 @@ var TabsProgressListener = { gBrowser.getNotificationBox(aBrowser).removeTransientNotifications(); FullZoom.onLocationChange(aLocationURI, false, aBrowser); + + ContentBlocking.onLocationChange(); }, onLinkIconAvailable(browser, dataURI, iconURI) { diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml index 11b0553fa95b..2f028021baf6 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -81,6 +81,7 @@ /* eslint-env mozilla/browser-window */ Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this); Services.scriptloader.loadSubScript("chrome://browser/content/browser-captivePortal.js", this); + Services.scriptloader.loadSubScript("chrome://browser/content/browser-contentblocking.js", this); if (AppConstants.MOZ_DATA_REPORTING) { Services.scriptloader.loadSubScript("chrome://browser/content/browser-data-submission-info-bar.js", this); } diff --git a/browser/base/content/test/siteIdentity/browser.ini b/browser/base/content/test/siteIdentity/browser.ini index e405118935e9..da2c04e1b299 100644 --- a/browser/base/content/test/siteIdentity/browser.ini +++ b/browser/base/content/test/siteIdentity/browser.ini @@ -52,7 +52,6 @@ support-files = ../permissions/permissions.html [browser_identityPopup_clearSiteData.js] [browser_identityPopup_custom_roots.js] [browser_identityPopup_focus.js] -[browser_identityPopup_telemetry.js] [browser_insecureLoginForms.js] support-files = insecure_opener.html diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_telemetry.js b/browser/base/content/test/siteIdentity/browser_identityPopup_telemetry.js deleted file mode 100644 index c02ff1b4238e..000000000000 --- a/browser/base/content/test/siteIdentity/browser_identityPopup_telemetry.js +++ /dev/null @@ -1,43 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -const BENIGN_PAGE = - "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html"; - -/** - * Enable local telemetry recording for the duration of the tests. - */ -var oldCanRecord = Services.telemetry.canRecordExtended; -Services.telemetry.canRecordExtended = true; -registerCleanupFunction(function() { - Services.telemetry.canRecordExtended = oldCanRecord; -}); - -add_task(async function testIdentityPopupEvents() { - let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); - - await promiseTabLoadEvent(tab, BENIGN_PAGE); - - Services.telemetry.clearEvents(); - - let shown = BrowserTestUtils.waitForEvent( - gIdentityHandler._identityPopup, - "popupshown" - ); - EventUtils.synthesizeMouseAtCenter(gIdentityHandler._identityBox, {}); - await shown; - - let events = Services.telemetry.snapshotEvents( - Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, - true - ).parent; - let openEvents = events.filter( - e => - e[1] == "security.ui.identitypopup" && - e[2] == "open" && - e[3] == "identity_popup" - ); - is(openEvents.length, 1, "recorded telemetry for opening the identity popup"); - - gBrowser.removeCurrentTab(); -}); diff --git a/browser/base/content/test/siteProtections/browser_protections_UI.js b/browser/base/content/test/siteProtections/browser_protections_UI.js index 1232e6dc11c5..d088594687bf 100644 --- a/browser/base/content/test/siteProtections/browser_protections_UI.js +++ b/browser/base/content/test/siteProtections/browser_protections_UI.js @@ -3,12 +3,6 @@ /* Basic UI tests for the protections panel */ -ChromeUtils.defineModuleGetter( - this, - "ContentBlockingAllowList", - "resource://gre/modules/ContentBlockingAllowList.jsm" -); - add_task(async function setup() { await SpecialPowers.pushPrefEnv({ set: [ @@ -60,7 +54,43 @@ add_task(async function testToggleSwitch() { !gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("enabled"), "TP Switch should be disabled" ); - ContentBlockingAllowList.remove(tab.linkedBrowser); + Services.perms.remove( + ContentBlocking._baseURIForChannelClassifier, + "trackingprotection" + ); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function testSiteNotWorking() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "https://example.com" + ); + await openProtectionsPanel(); + let viewShownPromise = BrowserTestUtils.waitForEvent( + gProtectionsHandler._protectionsPopupMultiView, + "ViewShown" + ); + document.getElementById("protections-popup-tp-switch-breakage-link").click(); + let event = await viewShownPromise; + is( + event.originalTarget.id, + "protections-popup-siteNotWorkingView", + "Site Not Working? view should be shown" + ); + viewShownPromise = BrowserTestUtils.waitForEvent( + gProtectionsHandler._protectionsPopupMultiView, + "ViewShown" + ); + document + .getElementById("protections-popup-siteNotWorkingView-sendReport") + .click(); + event = await viewShownPromise; + is( + event.originalTarget.id, + "protections-popup-sendReportView", + "Send Report view should be shown" + ); BrowserTestUtils.removeTab(tab); }); @@ -254,6 +284,9 @@ add_task(async function testToggleSwitchFlow() { await popuphiddenPromise; // Clean up the TP state. - ContentBlockingAllowList.remove(tab.linkedBrowser); + Services.perms.remove( + ContentBlocking._baseURIForChannelClassifier, + "trackingprotection" + ); BrowserTestUtils.removeTab(tab); }); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js b/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js index 8b638015fa4e..a0c37042c391 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js @@ -34,13 +34,10 @@ async function testTrackingProtectionAnimation(tabbrowser) { tabbrowser, BENIGN_PAGE ); - let gProtectionsHandler = tabbrowser.ownerGlobal.gProtectionsHandler; + let ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking; - ok(!gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox not active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "iconBox not active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Load a test page containing tracking elements"); let trackingTab = await BrowserTestUtils.openNewForegroundTab( @@ -48,10 +45,10 @@ async function testTrackingProtectionAnimation(tabbrowser) { TRACKING_PAGE ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok(gProtectionsHandler.iconBox.hasAttribute("animate"), "iconBox animating"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating"); await BrowserTestUtils.waitForEvent( - gProtectionsHandler.animatedIcon, + ContentBlocking.animatedIcon, "animationend" ); @@ -61,10 +58,10 @@ async function testTrackingProtectionAnimation(tabbrowser) { COOKIE_PAGE ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok(gProtectionsHandler.iconBox.hasAttribute("animate"), "iconBox animating"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating"); await BrowserTestUtils.waitForEvent( - gProtectionsHandler.animatedIcon, + ContentBlocking.animatedIcon, "animationend" ); @@ -73,33 +70,24 @@ async function testTrackingProtectionAnimation(tabbrowser) { tabbrowser.selectedTab = benignTab; await securityChanged; - ok(!gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox not active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "iconBox not active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Switch from benign -> tracking tab"); securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal); tabbrowser.selectedTab = trackingTab; await securityChanged; - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Switch from tracking -> tracking cookies tab"); securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal); tabbrowser.selectedTab = trackingCookiesTab; await securityChanged; - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Reload tracking cookies tab"); securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal); @@ -110,10 +98,10 @@ async function testTrackingProtectionAnimation(tabbrowser) { tabbrowser.reload(); await Promise.all([securityChanged, contentBlockingEvent]); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok(gProtectionsHandler.iconBox.hasAttribute("animate"), "iconBox animating"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating"); await BrowserTestUtils.waitForEvent( - gProtectionsHandler.animatedIcon, + ContentBlocking.animatedIcon, "animationend" ); @@ -124,10 +112,10 @@ async function testTrackingProtectionAnimation(tabbrowser) { tabbrowser.reload(); await Promise.all([securityChanged, contentBlockingEvent]); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok(gProtectionsHandler.iconBox.hasAttribute("animate"), "iconBox animating"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(ContentBlocking.iconBox.hasAttribute("animate"), "iconBox animating"); await BrowserTestUtils.waitForEvent( - gProtectionsHandler.animatedIcon, + ContentBlocking.animatedIcon, "animationend" ); @@ -140,11 +128,8 @@ async function testTrackingProtectionAnimation(tabbrowser) { let result = await Promise.race([securityChanged, timeoutPromise]); is(result, undefined, "No securityChange events should be received"); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Inject tracking element inside tracking tab"); securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal); @@ -155,11 +140,8 @@ async function testTrackingProtectionAnimation(tabbrowser) { result = await Promise.race([securityChanged, timeoutPromise]); is(result, undefined, "No securityChange events should be received"); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); tabbrowser.selectedTab = trackingCookiesTab; @@ -172,11 +154,8 @@ async function testTrackingProtectionAnimation(tabbrowser) { result = await Promise.race([securityChanged, timeoutPromise]); is(result, undefined, "No securityChange events should be received"); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); info("Inject tracking element inside tracking cookies tab"); securityChanged = waitForSecurityChange(1, tabbrowser.ownerGlobal); @@ -187,11 +166,8 @@ async function testTrackingProtectionAnimation(tabbrowser) { result = await Promise.race([securityChanged, timeoutPromise]); is(result, undefined, "No securityChange events should be received"); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "iconBox active"); - ok( - !gProtectionsHandler.iconBox.hasAttribute("animate"), - "iconBox not animating" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "iconBox active"); + ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating"); while (tabbrowser.tabs.length > 1) { tabbrowser.removeCurrentTab(); @@ -201,11 +177,8 @@ async function testTrackingProtectionAnimation(tabbrowser) { add_task(async function testNormalBrowsing() { await UrlClassifierTestUtils.addTestTrackers(); - let gProtectionsHandler = gBrowser.ownerGlobal.gProtectionsHandler; - ok( - gProtectionsHandler, - "gProtectionsHandler is attached to the browser window" - ); + let ContentBlocking = gBrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the browser window"); let TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the browser window"); let ThirdPartyCookies = gBrowser.ownerGlobal.ThirdPartyCookies; @@ -231,11 +204,8 @@ add_task(async function testPrivateBrowsing() { }); let tabbrowser = privateWin.gBrowser; - let gProtectionsHandler = tabbrowser.ownerGlobal.gProtectionsHandler; - ok( - gProtectionsHandler, - "gProtectionsHandler is attached to the private window" - ); + let ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the private window"); let TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the private window"); let ThirdPartyCookies = tabbrowser.ownerGlobal.ThirdPartyCookies; diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_background_tabs.js b/browser/base/content/test/trackingUI/browser_trackingUI_background_tabs.js index a7edac71cb5f..d6fc1f1a2651 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_background_tabs.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_background_tabs.js @@ -46,10 +46,7 @@ add_task(async function testBackgroundTabs() { "Foreground tab has the correct content blocking event." ); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); await BrowserTestUtils.switchTab(gBrowser, backgroundTab); @@ -65,7 +62,7 @@ add_task(async function testBackgroundTabs() { "Foreground tab still has the correct content blocking event." ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); gBrowser.removeTab(backgroundTab); gBrowser.removeTab(tab); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js b/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js index dd06f7ab6643..87900aecc82f 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js @@ -13,9 +13,13 @@ const CONTAINER_PAGE = const TPC_PREF = "network.cookie.cookieBehavior"; add_task(async function setup() { + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + await UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(() => { + Services.telemetry.canRecordExtended = oldCanRecord; UrlClassifierTestUtils.cleanupTestTrackers(); }); }); @@ -44,7 +48,9 @@ async function assertSitesListed( let [tab] = await Promise.all([promise, waitForContentBlockingEvent(count)]); let browser = tab.linkedBrowser; - await openProtectionsPopup(); + await openIdentityPopup(); + + Services.telemetry.clearEvents(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cookies" @@ -57,6 +63,17 @@ async function assertSitesListed( ok(true, "Cookies view was shown"); + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + let buttonEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "cookies_subview_btn" + ); + is(buttonEvents.length, 1, "recorded telemetry for the button click"); + let listHeaders = cookiesView.querySelectorAll( ".identity-popup-cookiesView-list-header" ); @@ -112,7 +129,7 @@ async function assertSitesListed( ); } - let mainView = document.getElementById("protections-popup-mainView"); + let mainView = document.getElementById("identity-popup-mainView"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); let backButton = cookiesView.querySelector(".subviewbutton-back"); backButton.click(); @@ -284,7 +301,7 @@ add_task(async function testCookiesSubViewAllowed() { }); let [tab] = await Promise.all([promise, waitForContentBlockingEvent(3)]); - await openProtectionsPopup(); + await openIdentityPopup(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cookies" @@ -400,7 +417,7 @@ add_task(async function testCookiesSubViewAllowedHeuristic() { await new Promise(resolve => waitForFocus(resolve, popup)); await new Promise(resolve => waitForFocus(resolve, window)); - await openProtectionsPopup(); + await openIdentityPopup(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cookies" @@ -483,7 +500,7 @@ add_task(async function testCookiesSubViewBlockedDoublyNested() { }); let [tab] = await Promise.all([promise, waitForContentBlockingEvent(3)]); - await openProtectionsPopup(); + await openIdentityPopup(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cookies" diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js b/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js index c76f1f4e288b..c24b6548809a 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js @@ -1,3 +1,4 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ @@ -49,22 +50,22 @@ async function testIdentityState(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.disableForCurrentPage(); + ContentBlocking.disableForCurrentPage(); await loaded; } ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("detected"), + !ContentBlocking.content.hasAttribute("detected"), "cryptominers are not detected" ); if (hasException) { ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + !BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is visible to indicate the exception" ); } else { ok( - BrowserTestUtils.is_hidden(gProtectionsHandler.iconBox), + BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is not visible" ); } @@ -77,16 +78,13 @@ async function testIdentityState(hasException) { await promise; + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible" ); is( - gProtectionsHandler.iconBox.hasAttribute("hasException"), + ContentBlocking.iconBox.hasAttribute("hasException"), hasException, "Shows an exception when appropriate" ); @@ -97,7 +95,7 @@ async function testIdentityState(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.enableForCurrentPage(); + ContentBlocking.enableForCurrentPage(); await loaded; } @@ -121,7 +119,7 @@ async function testSubview(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.disableForCurrentPage(); + ContentBlocking.disableForCurrentPage(); await loaded; } @@ -131,7 +129,7 @@ async function testSubview(hasException) { }); await promise; - await openProtectionsPopup(); + await openIdentityPopup(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cryptominers" @@ -159,7 +157,7 @@ async function testSubview(hasException) { "Indicates the miner was blocked or allowed" ); - let mainView = document.getElementById("protections-popup-mainView"); + let mainView = document.getElementById("identity-popup-mainView"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); let backButton = subview.querySelector(".subviewbutton-back"); backButton.click(); @@ -173,7 +171,7 @@ async function testSubview(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.enableForCurrentPage(); + ContentBlocking.enableForCurrentPage(); await loaded; } diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js b/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js index c5166098c80f..2c94546e5ead 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_fetch.js @@ -18,19 +18,16 @@ add_task(async function test_fetch() { }); await contentBlockingEvent; - let gProtectionsHandler = newTabBrowser.ownerGlobal.gProtectionsHandler; - ok(gProtectionsHandler, "got CB object"); + let ContentBlocking = newTabBrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "got CB object"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), + ContentBlocking.content.hasAttribute("detected"), "has detected content blocking" ); - ok( - gProtectionsHandler.iconBox.hasAttribute("active"), - "icon box is active" - ); + ok(ContentBlocking.iconBox.hasAttribute("active"), "icon box is active"); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip" ); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js b/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js index 02ea9ca60bb1..79d403509beb 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js @@ -1,3 +1,4 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ @@ -49,22 +50,22 @@ async function testIdentityState(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.disableForCurrentPage(); + ContentBlocking.disableForCurrentPage(); await loaded; } ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("detected"), + !ContentBlocking.content.hasAttribute("detected"), "fingerprinters are not detected" ); if (hasException) { ok( - !BrowserTestUtils.is_hidden(gProtectionsHandler.iconBox), + !BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is visible to indicate the exception" ); } else { ok( - BrowserTestUtils.is_hidden(gProtectionsHandler.iconBox), + BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is not visible" ); } @@ -77,16 +78,13 @@ async function testIdentityState(hasException) { await promise; + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible" ); is( - gProtectionsHandler.iconBox.hasAttribute("hasException"), + ContentBlocking.iconBox.hasAttribute("hasException"), hasException, "Shows an exception when appropriate" ); @@ -97,7 +95,7 @@ async function testIdentityState(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.enableForCurrentPage(); + ContentBlocking.enableForCurrentPage(); await loaded; } @@ -121,7 +119,7 @@ async function testSubview(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.disableForCurrentPage(); + ContentBlocking.disableForCurrentPage(); await loaded; } @@ -131,7 +129,7 @@ async function testSubview(hasException) { }); await promise; - await openProtectionsPopup(); + await openIdentityPopup(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-fingerprinters" @@ -159,7 +157,7 @@ async function testSubview(hasException) { "Indicates the fingerprinter was blocked or allowed" ); - let mainView = document.getElementById("protections-popup-mainView"); + let mainView = document.getElementById("identity-popup-mainView"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); let backButton = subview.querySelector(".subviewbutton-back"); backButton.click(); @@ -173,7 +171,7 @@ async function testSubview(hasException) { false, TRACKING_PAGE ); - gProtectionsHandler.enableForCurrentPage(); + ContentBlocking.enableForCurrentPage(); await loaded; } diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_open_preferences.js b/browser/base/content/test/trackingUI/browser_trackingUI_open_preferences.js index 2ec98ecc3c1b..a7ffedb8c54d 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_open_preferences.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_open_preferences.js @@ -8,11 +8,9 @@ const TPC_PREF = "network.cookie.cookieBehavior"; const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html"; -async function waitAndAssertPreferencesShown(_spotlight, identityPopup) { +async function waitAndAssertPreferencesShown(_spotlight) { await BrowserTestUtils.waitForEvent( - identityPopup - ? gIdentityHandler._identityPopup - : gProtectionsHandler._protectionsPopup, + gIdentityHandler._identityPopup, "popuphidden" ); await TestUtils.waitForCondition( @@ -51,11 +49,16 @@ add_task(async function setup() { }); }); -// Tests that pressing the content blocking preferences icon in the protections popup +// Tests that pressing the content blocking preferences icon in the identity popup // links to about:preferences add_task(async function testOpenPreferencesFromCBPrefsButton() { await BrowserTestUtils.withNewTab("https://example.com", async function() { - await openProtectionsPopup(); + let promisePanelOpen = BrowserTestUtils.waitForEvent( + gIdentityHandler._identityPopup, + "popupshown" + ); + gIdentityHandler._identityBox.click(); + await promisePanelOpen; let preferencesButton = document.getElementById( "tracking-protection-preferences-button" @@ -66,9 +69,23 @@ add_task(async function testOpenPreferencesFromCBPrefsButton() { "The preferences button is shown." ); + Services.telemetry.clearEvents(); + let shown = waitAndAssertPreferencesShown("trackingprotection"); preferencesButton.click(); await shown; + + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + let clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "cb_prefs_button" + ); + is(clickEvents.length, 1, "recorded telemetry for the click"); }); }); @@ -76,7 +93,12 @@ add_task(async function testOpenPreferencesFromCBPrefsButton() { // links to about:preferences add_task(async function testOpenPreferencesFromPermissionsPrefsButton() { await BrowserTestUtils.withNewTab("https://example.com", async function() { - await openIdentityPopup(); + let promisePanelOpen = BrowserTestUtils.waitForEvent( + gIdentityHandler._identityPopup, + "popupshown" + ); + gIdentityHandler._identityBox.click(); + await promisePanelOpen; let preferencesButton = document.getElementById( "identity-popup-permission-preferences-button" @@ -89,7 +111,7 @@ add_task(async function testOpenPreferencesFromPermissionsPrefsButton() { Services.telemetry.clearEvents(); - let shown = waitAndAssertPreferencesShown("permissions", true); + let shown = waitAndAssertPreferencesShown("permissions"); preferencesButton.click(); await shown; @@ -113,7 +135,12 @@ add_task(async function testOpenPreferencesFromTrackersSubview() { Services.prefs.setBoolPref(TP_PREF, true); await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function() { - await openProtectionsPopup(); + let promisePanelOpen = BrowserTestUtils.waitForEvent( + gIdentityHandler._identityPopup, + "popupshown" + ); + gIdentityHandler._identityBox.click(); + await promisePanelOpen; let categoryItem = document.getElementById( "identity-popup-content-blocking-category-tracking-protection" @@ -138,9 +165,23 @@ add_task(async function testOpenPreferencesFromTrackersSubview() { "The preferences button is shown." ); + Services.telemetry.clearEvents(); + let shown = waitAndAssertPreferencesShown("trackingprotection"); preferencesButton.click(); await shown; + + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + let clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "trackers_prefs_btn" + ); + is(clickEvents.length, 1, "recorded telemetry for the click"); }); Services.prefs.clearUserPref(TP_PREF); @@ -155,7 +196,12 @@ add_task(async function testOpenPreferencesFromCookiesSubview() { ); await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function() { - await openProtectionsPopup(); + let promisePanelOpen = BrowserTestUtils.waitForEvent( + gIdentityHandler._identityPopup, + "popupshown" + ); + gIdentityHandler._identityBox.click(); + await promisePanelOpen; let categoryItem = document.getElementById( "identity-popup-content-blocking-category-cookies" @@ -180,9 +226,23 @@ add_task(async function testOpenPreferencesFromCookiesSubview() { "The preferences button is shown." ); + Services.telemetry.clearEvents(); + let shown = waitAndAssertPreferencesShown("trackingprotection"); preferencesButton.click(); await shown; + + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + let clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "cookies_prefs_btn" + ); + is(clickEvents.length, 1, "recorded telemetry for the click"); }); Services.prefs.clearUserPref(TPC_PREF); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js index 5a7a33b95170..fc7cb2f068af 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js @@ -9,13 +9,13 @@ const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html"; const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications"; var TrackingProtection = null; -var gProtectionsHandler = null; +var ContentBlocking = null; var browser = null; registerCleanupFunction(function() { Services.prefs.clearUserPref(TP_PB_PREF); Services.prefs.clearUserPref(DTSCBN_PREF); - gProtectionsHandler = TrackingProtection = browser = null; + ContentBlocking = TrackingProtection = browser = null; UrlClassifierTestUtils.cleanupTestTrackers(); }); @@ -26,9 +26,9 @@ function hidden(sel) { return display === "none"; } -function protectionsPopupState() { +function identityPopupState() { let win = browser.ownerGlobal; - return win.document.getElementById("protections-popup").state; + return win.document.getElementById("identity-popup").state; } function clickButton(sel) { @@ -39,30 +39,43 @@ function clickButton(sel) { function testTrackingPage(window) { info("Tracking content must be blocked"); + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + !ContentBlocking.content.hasAttribute("hasException"), "content shows no exception" ); ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible" ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); ok( - !gProtectionsHandler.iconBox.hasAttribute("hasException"), + !ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception" ); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip" ); + ok(hidden("#tracking-action-block"), "blockButton is hidden"); + + if (PrivateBrowsingUtils.isWindowPrivate(window)) { + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); + ok( + !hidden("#tracking-action-unblock-private"), + "unblockButtonPrivate is visible" + ); + } else { + ok(!hidden("#tracking-action-unblock"), "unblockButton is visible"); + ok( + hidden("#tracking-action-unblock-private"), + "unblockButtonPrivate is hidden" + ); + } + ok( hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden" @@ -75,30 +88,29 @@ function testTrackingPage(window) { function testTrackingPageUnblocked() { info("Tracking content must be white-listed and not blocked"); + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + ContentBlocking.content.hasAttribute("hasException"), "content shows exception" ); - ok(!gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); ok( - gProtectionsHandler.iconBox.hasAttribute("hasException"), + ContentBlocking.iconBox.hasAttribute("hasException"), "shield shows exception" ); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip" ); ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible" ); + ok(!hidden("#tracking-action-block"), "blockButton is visible"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok( hidden("#identity-popup-content-blocking-not-detected"), @@ -123,8 +135,8 @@ add_task(async function testExceptionAddition() { waitForStateStop: true, }); - gProtectionsHandler = browser.ownerGlobal.gProtectionsHandler; - ok(gProtectionsHandler, "CB is attached to the private window"); + ContentBlocking = browser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the private window"); TrackingProtection = browser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the private window"); @@ -141,8 +153,8 @@ add_task(async function testExceptionAddition() { info("Disable TP for the page (which reloads the page)"); let tabReloadPromise = promiseTabLoadEvent(tab); - gProtectionsHandler.disableForCurrentPage(); - is(protectionsPopupState(), "closed", "protections popup is closed"); + clickButton("#tracking-action-unblock"); + is(identityPopupState(), "closed", "Identity popup is closed"); await tabReloadPromise; testTrackingPageUnblocked(); @@ -171,8 +183,8 @@ add_task(async function testExceptionPersistence() { waitForStateStop: true, }); - gProtectionsHandler = browser.ownerGlobal.gProtectionsHandler; - ok(gProtectionsHandler, "CB is attached to the private window"); + ContentBlocking = browser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the private window"); TrackingProtection = browser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the private window"); @@ -188,8 +200,8 @@ add_task(async function testExceptionPersistence() { info("Disable TP for the page (which reloads the page)"); let tabReloadPromise = promiseTabLoadEvent(tab); - gProtectionsHandler.disableForCurrentPage(); - is(protectionsPopupState(), "closed", "protections popup is closed"); + clickButton("#tracking-action-unblock"); + is(identityPopupState(), "closed", "Identity popup is closed"); await Promise.all([ tabReloadPromise, diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_report_breakage.js b/browser/base/content/test/trackingUI/browser_trackingUI_report_breakage.js index d011e367781c..e9840173b213 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_report_breakage.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_report_breakage.js @@ -28,7 +28,12 @@ let { Preferences } = ChromeUtils.import( add_task(async function setup() { await UrlClassifierTestUtils.addTestTrackers(); + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + registerCleanupFunction(() => { + Services.telemetry.canRecordExtended = oldCanRecord; + // Clear prefs that are touched in this test again for sanity. Services.prefs.clearUserPref(TP_PREF); Services.prefs.clearUserPref(CB_PREF); @@ -127,10 +132,10 @@ add_task(async function testReportBreakageVisibility() { } await BrowserTestUtils.withNewTab(scenario.url, async function() { - await openProtectionsPopup(); + await openIdentityPopup(); let reportBreakageButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" + "identity-popup-content-blocking-report-breakage" ); await TestUtils.waitForCondition( () => @@ -153,40 +158,44 @@ add_task(async function testReportBreakageCancel() { Services.prefs.setBoolPref(PREF_REPORT_BREAKAGE_ENABLED, true); await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function() { - await openProtectionsPopup(); + await openIdentityPopup(); - let siteNotWorkingButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" + Services.telemetry.clearEvents(); + + let reportBreakageButton = document.getElementById( + "identity-popup-content-blocking-report-breakage" ); ok( - BrowserTestUtils.is_visible(siteNotWorkingButton), - "site not working button is visible" + BrowserTestUtils.is_visible(reportBreakageButton), + "report breakage button is visible" ); - let siteNotWorkingView = document.getElementById( - "protections-popup-siteNotWorkingView" + let reportBreakageView = document.getElementById( + "identity-popup-breakageReportView" ); let viewShown = BrowserTestUtils.waitForEvent( - siteNotWorkingView, + reportBreakageView, "ViewShown" ); - siteNotWorkingButton.click(); + reportBreakageButton.click(); await viewShown; - let sendReportButton = document.getElementById( - "protections-popup-siteNotWorkingView-sendReport" + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + let clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "report_breakage" ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - sendReportButton.click(); - await viewShown; + is(clickEvents.length, 1, "recorded telemetry for the click"); ok(true, "Report breakage view was shown"); - viewShown = BrowserTestUtils.waitForEvent(siteNotWorkingView, "ViewShown"); + let mainView = document.getElementById("identity-popup-mainView"); + viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); let cancelButton = document.getElementById( - "protections-popup-sendReportView-cancel" + "identity-popup-breakageReportView-cancel" ); cancelButton.click(); await viewShown; @@ -268,54 +277,42 @@ async function testReportBreakage(url, tags) { Services.prefs.setBoolPref(PREF_REPORT_BREAKAGE_ENABLED, true); Services.prefs.setStringPref(PREF_REPORT_BREAKAGE_URL, path); - await openProtectionsPopup(); - - let siteNotWorkingButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" - ); - await TestUtils.waitForCondition( - () => BrowserTestUtils.is_visible(siteNotWorkingButton), - "site not working button is visible" - ); - let siteNotWorkingView = document.getElementById( - "protections-popup-siteNotWorkingView" - ); - let viewShown = BrowserTestUtils.waitForEvent( - siteNotWorkingView, - "ViewShown" - ); - siteNotWorkingButton.click(); - await viewShown; - - let sendReportButton = document.getElementById( - "protections-popup-siteNotWorkingView-sendReport" - ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - sendReportButton.click(); - await viewShown; - - ok(true, "Report breakage view was shown"); + await openIdentityPopup(); let comments = document.getElementById( - "protections-popup-sendReportView-collection-comments" + "identity-popup-breakageReportView-collection-comments" ); is(comments.value, "", "Comments textarea should initially be empty"); + let reportBreakageButton = document.getElementById( + "identity-popup-content-blocking-report-breakage" + ); + await TestUtils.waitForCondition( + () => BrowserTestUtils.is_visible(reportBreakageButton), + "report breakage button is visible" + ); + let reportBreakageView = document.getElementById( + "identity-popup-breakageReportView" + ); + let viewShown = BrowserTestUtils.waitForEvent( + reportBreakageView, + "ViewShown" + ); + reportBreakageButton.click(); + await viewShown; + let submitButton = document.getElementById( - "protections-popup-sendReportView-submit" + "identity-popup-breakageReportView-submit" ); let reportURL = document.getElementById( - "protections-popup-sendReportView-collection-url" + "identity-popup-breakageReportView-collection-url" ).value; is(reportURL, url, "Shows the correct URL in the report UI."); // Make sure that sending the report closes the identity popup. let popuphidden = BrowserTestUtils.waitForEvent( - gProtectionsHandler._protectionsPopup, + gIdentityHandler._identityPopup, "popuphidden" ); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_state.js b/browser/base/content/test/trackingUI/browser_trackingUI_state.js index 57165dec2363..9aa2083b5450 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js @@ -24,14 +24,14 @@ const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html"; const COOKIE_PAGE = "http://not-tracking.example.com/browser/browser/base/content/test/trackingUI/cookiePage.html"; -var gProtectionsHandler = null; +var ContentBlocking = null; var TrackingProtection = null; var ThirdPartyCookies = null; var tabbrowser = null; var gTrackingPageURL = TRACKING_PAGE; registerCleanupFunction(function() { - TrackingProtection = gProtectionsHandler = ThirdPartyCookies = tabbrowser = null; + TrackingProtection = ContentBlocking = ThirdPartyCookies = tabbrowser = null; UrlClassifierTestUtils.cleanupTestTrackers(); Services.prefs.clearUserPref(TP_PREF); Services.prefs.clearUserPref(TP_PB_PREF); @@ -59,31 +59,27 @@ function clickButton(sel) { function testBenignPage() { info("Non-tracking content must not be blocked"); ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("detected"), + !ContentBlocking.content.hasAttribute("detected"), "no trackers are detected" ); ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + !ContentBlocking.content.hasAttribute("hasException"), "content shows no exception" ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); - ok( - !gProtectionsHandler.iconBox.hasAttribute("hasException"), + !ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception" ); ok( - !gProtectionsHandler.iconBox.hasAttribute("tooltiptext"), + !ContentBlocking.iconBox.hasAttribute("tooltiptext"), "icon box has no tooltip" ); - ok( - BrowserTestUtils.is_hidden(gProtectionsHandler.iconBox), - "icon box is hidden" - ); + ok(BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is hidden"); + ok(hidden("#tracking-action-block"), "blockButton is hidden"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok( !hidden("#identity-popup-content-blocking-not-detected"), @@ -106,32 +102,31 @@ function testBenignPage() { function testBenignPageWithException() { info("Non-tracking content must not be blocked"); ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("detected"), + !ContentBlocking.content.hasAttribute("detected"), "no trackers are detected" ); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + ContentBlocking.content.hasAttribute("hasException"), "content shows exception" ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); - ok( - gProtectionsHandler.iconBox.hasAttribute("hasException"), + ContentBlocking.iconBox.hasAttribute("hasException"), "shield shows exception" ); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip" ); ok( - !BrowserTestUtils.is_hidden(gProtectionsHandler.iconBox), + !BrowserTestUtils.is_hidden(ContentBlocking.iconBox), "icon box is not hidden" ); + ok(!hidden("#tracking-action-block"), "blockButton is visible"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok( !hidden("#identity-popup-content-blocking-not-detected"), @@ -163,39 +158,57 @@ function areTrackersBlocked(isPrivateBrowsing) { function testTrackingPage(window) { info("Tracking content must be blocked"); + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - !gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + !ContentBlocking.content.hasAttribute("hasException"), "content shows no exception" ); let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window); let blockedByTP = areTrackersBlocked(isWindowPrivate); is( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockedByTP, "icon box is" + (blockedByTP ? "" : " not") + " visible" ); is( - gProtectionsHandler.iconBox.hasAttribute("active"), + ContentBlocking.iconBox.hasAttribute("active"), blockedByTP, "shield is" + (blockedByTP ? "" : " not") + " active" ); ok( - !gProtectionsHandler.iconBox.hasAttribute("hasException"), + !ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception" ); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), blockedByTP ? gNavigatorBundle.getString("trackingProtection.icon.activeTooltip") : "", "correct tooltip" ); + ok(hidden("#tracking-action-block"), "blockButton is hidden"); + + if (isWindowPrivate) { + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); + is( + !hidden("#tracking-action-unblock-private"), + blockedByTP, + "unblockButtonPrivate is" + (blockedByTP ? "" : " not") + " visible" + ); + } else { + ok( + hidden("#tracking-action-unblock-private"), + "unblockButtonPrivate is hidden" + ); + is( + !hidden("#tracking-action-unblock"), + blockedByTP, + "unblockButton is" + (blockedByTP ? "" : " not") + " visible" + ); + } + ok( hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden" @@ -224,33 +237,29 @@ function testTrackingPage(window) { function testTrackingPageUnblocked(blockedByTP, window) { info("Tracking content must be white-listed and not blocked"); + ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected"); ok( - gProtectionsHandler._protectionsPopup.hasAttribute("detected"), - "trackers are detected" - ); - ok( - gProtectionsHandler._protectionsPopup.hasAttribute("hasException"), + ContentBlocking.content.hasAttribute("hasException"), "content shows exception" ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); - ok( - gProtectionsHandler.iconBox.hasAttribute("hasException"), + ContentBlocking.iconBox.hasAttribute("hasException"), "shield shows exception" ); is( - gProtectionsHandler.iconBox.getAttribute("tooltiptext"), + ContentBlocking.iconBox.getAttribute("tooltiptext"), gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip" ); ok( - BrowserTestUtils.is_visible(gProtectionsHandler.iconBox), + BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible" ); + ok(!hidden("#tracking-action-block"), "blockButton is visible"); + ok(hidden("#tracking-action-unblock"), "unblockButton is hidden"); ok( hidden("#identity-popup-content-blocking-not-detected"), @@ -306,7 +315,7 @@ async function testContentBlocking(tab) { info("Disable CB for the page (which reloads the page)"); let tabReloadPromise = promiseTabLoadEvent(tab); - tab.ownerGlobal.gProtectionsHandler.disableForCurrentPage(); + clickButton("#tracking-action-unblock"); await tabReloadPromise; let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(tab.ownerGlobal); let blockedByTP = areTrackersBlocked(isPrivateBrowsing); @@ -314,7 +323,7 @@ async function testContentBlocking(tab) { info("Re-enable TP for the page (which reloads the page)"); tabReloadPromise = promiseTabLoadEvent(tab); - tab.ownerGlobal.gProtectionsHandler.enableForCurrentPage(); + clickButton("#tracking-action-block"); await tabReloadPromise; testTrackingPage(tab.ownerGlobal); } @@ -327,11 +336,8 @@ add_task(async function testNormalBrowsing() { tabbrowser = gBrowser; let tab = (tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser)); - gProtectionsHandler = gBrowser.ownerGlobal.gProtectionsHandler; - ok( - gProtectionsHandler, - "gProtectionsHandler is attached to the browser window" - ); + ContentBlocking = gBrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the browser window"); TrackingProtection = gBrowser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the browser window"); is( @@ -366,11 +372,8 @@ add_task(async function testPrivateBrowsing() { Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT); - gProtectionsHandler = tabbrowser.ownerGlobal.gProtectionsHandler; - ok( - gProtectionsHandler, - "gProtectionsHandler is attached to the private window" - ); + ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the private window"); TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection; ok(TrackingProtection, "TP is attached to the private window"); is( @@ -398,11 +401,8 @@ add_task(async function testThirdPartyCookies() { tabbrowser = gBrowser; let tab = (tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser)); - gProtectionsHandler = gBrowser.ownerGlobal.gProtectionsHandler; - ok( - gProtectionsHandler, - "gProtectionsHandler is attached to the browser window" - ); + ContentBlocking = gBrowser.ownerGlobal.ContentBlocking; + ok(ContentBlocking, "CB is attached to the browser window"); ThirdPartyCookies = gBrowser.ownerGlobal.ThirdPartyCookies; ok(ThirdPartyCookies, "TP is attached to the browser window"); is( diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js b/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js index 93222c44453f..99fbbd90eebf 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js @@ -23,10 +23,7 @@ add_task(async function testResetOnLocationChange() { 0, "Benign page has no content blocking event" ); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); await Promise.all([ promiseTabLoadEvent(tab, TRACKING_PAGE), @@ -38,7 +35,7 @@ add_task(async function testResetOnLocationChange() { Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT, "Tracking page has a content blocking event" ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); await promiseTabLoadEvent(tab, BENIGN_PAGE); @@ -47,10 +44,7 @@ add_task(async function testResetOnLocationChange() { 0, "Benign page has no content blocking event" ); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); let contentBlockingEvent = waitForContentBlockingEvent(3); let trackingTab = await BrowserTestUtils.openNewForegroundTab( @@ -64,7 +58,7 @@ add_task(async function testResetOnLocationChange() { Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT, "Tracking page has a content blocking event" ); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); gBrowser.selectedTab = tab; is( @@ -72,10 +66,7 @@ add_task(async function testResetOnLocationChange() { 0, "Benign page has no content blocking event" ); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); gBrowser.removeTab(trackingTab); gBrowser.removeTab(tab); @@ -89,22 +80,16 @@ add_task(async function testResetOnTabChange() { Services.prefs.setBoolPref(TP_PREF, true); let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ABOUT_PAGE); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); await Promise.all([ promiseTabLoadEvent(tab, TRACKING_PAGE), waitForContentBlockingEvent(3), ]); - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); await promiseTabLoadEvent(tab, ABOUT_PAGE); - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); let contentBlockingEvent = waitForContentBlockingEvent(3); let trackingTab = await BrowserTestUtils.openNewForegroundTab( @@ -112,13 +97,10 @@ add_task(async function testResetOnTabChange() { TRACKING_PAGE ); await contentBlockingEvent; - ok(gProtectionsHandler.iconBox.hasAttribute("active"), "shield is active"); + ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active"); gBrowser.selectedTab = tab; - ok( - !gProtectionsHandler.iconBox.hasAttribute("active"), - "shield is not active" - ); + ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active"); gBrowser.removeTab(trackingTab); gBrowser.removeTab(tab); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js b/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js index 2007aed892a2..f2d3a520eacf 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_telemetry.js @@ -59,7 +59,7 @@ add_task(async function testShieldHistogram() { info("Disable TP for the page (which reloads the page)"); let tabReloadPromise = promiseTabLoadEvent(tab); - gProtectionsHandler.disableForCurrentPage(); + document.querySelector("#tracking-action-unblock").doCommand(); await tabReloadPromise; is(getShieldCounts()[0], 3, "Adds one more page load"); is( @@ -70,7 +70,7 @@ add_task(async function testShieldHistogram() { info("Re-enable TP for the page (which reloads the page)"); tabReloadPromise = promiseTabLoadEvent(tab); - gProtectionsHandler.enableForCurrentPage(); + document.querySelector("#tracking-action-block").doCommand(); await tabReloadPromise; is(getShieldCounts()[0], 4, "Adds one more page load"); is( @@ -84,3 +84,78 @@ add_task(async function testShieldHistogram() { // Reset these to make counting easier for the next test getShieldHistogram().clear(); }); + +add_task(async function testIdentityPopupEvents() { + Services.prefs.setBoolPref(PREF, true); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + await promiseTabLoadEvent(tab, BENIGN_PAGE); + + Services.telemetry.clearEvents(); + + await openIdentityPopup(); + + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + let openEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "open" && + e[3] == "identity_popup" + ); + is(openEvents.length, 1, "recorded telemetry for opening the identity popup"); + is(openEvents[0][4], "shield-hidden", "recorded the shield as hidden"); + + await promiseTabLoadEvent(tab, TRACKING_PAGE); + + await openIdentityPopup(); + + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + openEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "open" && + e[3] == "identity_popup" + ); + is(openEvents.length, 1, "recorded telemetry for opening the identity popup"); + is(openEvents[0][4], "shield-showing", "recorded the shield as showing"); + + info("Disable TP for the page (which reloads the page)"); + let tabReloadPromise = promiseTabLoadEvent(tab); + document.querySelector("#tracking-action-unblock").doCommand(); + await tabReloadPromise; + + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + let clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "unblock" + ); + is(clickEvents.length, 1, "recorded telemetry for the click"); + + info("Re-enable TP for the page (which reloads the page)"); + tabReloadPromise = promiseTabLoadEvent(tab); + document.querySelector("#tracking-action-block").doCommand(); + await tabReloadPromise; + + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).parent; + clickEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && e[2] == "click" && e[3] == "block" + ); + is(clickEvents.length, 1, "recorded telemetry for the click"); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js b/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js index 3fbd49fbac95..f8c03241082a 100644 --- a/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js +++ b/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js @@ -10,16 +10,22 @@ const TRACKING_PAGE = const TP_PREF = "privacy.trackingprotection.enabled"; add_task(async function setup() { + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + await UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(() => { + Services.telemetry.canRecordExtended = oldCanRecord; UrlClassifierTestUtils.cleanupTestTrackers(); }); }); async function assertSitesListed(blocked) { await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function(browser) { - await openProtectionsPopup(); + await openIdentityPopup(); + + Services.telemetry.clearEvents(); let categoryItem = document.getElementById( "identity-popup-content-blocking-category-tracking-protection" @@ -35,6 +41,17 @@ async function assertSitesListed(blocked) { ok(true, "Trackers view was shown"); + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + let buttonEvents = events.filter( + e => + e[1] == "security.ui.identitypopup" && + e[2] == "click" && + e[3] == "trackers_subview_btn" + ); + is(buttonEvents.length, 1, "recorded telemetry for the button click"); + let listItems = trackersView.querySelectorAll( ".identity-popup-content-blocking-list-item" ); @@ -49,7 +66,7 @@ async function assertSitesListed(blocked) { "Strict info is hidden if TP is enabled." ); - let mainView = document.getElementById("protections-popup-mainView"); + let mainView = document.getElementById("identity-popup-mainView"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); let backButton = trackersView.querySelector(".subviewbutton-back"); backButton.click(); diff --git a/browser/base/content/test/trackingUI/head.js b/browser/base/content/test/trackingUI/head.js index a0b6adc71862..1aead5f03e52 100644 --- a/browser/base/content/test/trackingUI/head.js +++ b/browser/base/content/test/trackingUI/head.js @@ -2,12 +2,6 @@ var { UrlClassifierTestUtils } = ChromeUtils.import( "resource://testing-common/UrlClassifierTestUtils.jsm" ); -ChromeUtils.defineModuleGetter( - this, - "ContentBlockingAllowList", - "resource://gre/modules/ContentBlockingAllowList.jsm" -); - /** * Waits for a load (or custom) event to finish in a given tab. If provided * load an uri into the tab. @@ -49,14 +43,6 @@ function openIdentityPopup() { return viewShown; } -function openProtectionsPopup() { - let mainView = document.getElementById("protections-popup-mainView"); - let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); - // TODO: This should click on the icon once we have it. - gProtectionsHandler.showProtectionsPopup(); - return viewShown; -} - function waitForSecurityChange(numChanges = 1, win = null) { if (!win) { win = window; diff --git a/browser/base/jar.mn b/browser/base/jar.mn index 11057de4897d..9bb026fe117a 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -37,6 +37,7 @@ browser.jar: content/browser/browser-ctrlTab.js (content/browser-ctrlTab.js) content/browser/browser-customization.js (content/browser-customization.js) content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js) + content/browser/browser-contentblocking.js (content/browser-contentblocking.js) #ifndef MOZILLA_OFFICIAL content/browser/browser-development-helpers.js (content/browser-development-helpers.js) #endif diff --git a/browser/components/controlcenter/content/identityPanel.inc.xul b/browser/components/controlcenter/content/identityPanel.inc.xul index 88dfc0c85554..21e07888a260 100644 --- a/browser/components/controlcenter/content/identityPanel.inc.xul +++ b/browser/components/controlcenter/content/identityPanel.inc.xul @@ -56,6 +56,80 @@ oncommand="gIdentityHandler.showSecuritySubView(); gIdentityHandler.recordClick('security_subview_btn');"/> + + + + + + + + + &contentBlocking.detected; + &contentBlocking.notDetected; + + + + + + + + + + + + + + + + + + + + +