Bug 1634032 - use a template to wrap the protections popup while it's not needed, r=nhnt11

This also only updates per-category block information in the protections panel
while it's open or when it's about to be shown. To do this, the patch:

1. changes the `categoryItem` getter on all blockers to avoid memo'izing null when the popup is unavailable;
2. changes the `updateCategoryItem` method on all blockers to deal with `categoryItem` being null;
3. stops calling `updateCategoryItem` from the blockers' `init` method, instead delegating this responsibility
   to gProtectionsHandler doing so when the popup first becomes available. Note that we still need (2) because
   pref changes can trip us calling into `updateCategoryItem` anyway. We cannot avoid instantiating the pref
   getters because they are relied on by some of the `isDetected` and `isBlocking` implementations.
4. reorganizes `onContentBlockingEvent` so it updates the icon, reports telemetry, and updates internal state -
   but only updates the panel if it's visible, and otherwise simply memorizes the last event (which is just
   a number!);
5. ensures showing the panel updates the panel's category data based on the memorized state from (4).

Differential Revision: https://phabricator.services.mozilla.com/D81926
This commit is contained in:
Gijs Kruitbosch 2020-07-10 21:32:09 +00:00
Родитель f10844979b
Коммит 387e23f588
13 изменённых файлов: 352 добавлений и 185 удалений

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

@ -52,18 +52,27 @@ var Fingerprinting = {
false,
this.updateCategoryItem.bind(this)
);
this.updateCategoryItem();
},
get categoryItem() {
delete this.categoryItem;
return (this.categoryItem = document.getElementById(
let item = document.getElementById(
"protections-popup-category-fingerprinters"
));
);
if (item) {
delete this.categoryItem;
this.categoryItem = item;
}
return item;
},
updateCategoryItem() {
this.categoryItem.classList.toggle("blocked", this.enabled);
// Can't get `this.categoryItem` without the popup. Using the popup instead
// of `this.categoryItem` to guard access, because the category item getter
// can trigger bug 1543537. If there's no popup, we'll be called again the
// first time the popup shows.
if (gProtectionsHandler._protectionsPopup) {
this.categoryItem.classList.toggle("blocked", this.enabled);
}
},
get subView() {
@ -182,18 +191,27 @@ var Cryptomining = {
false,
this.updateCategoryItem.bind(this)
);
this.updateCategoryItem();
},
get categoryItem() {
delete this.categoryItem;
return (this.categoryItem = document.getElementById(
let item = document.getElementById(
"protections-popup-category-cryptominers"
));
);
if (item) {
delete this.categoryItem;
this.categoryItem = item;
}
return item;
},
updateCategoryItem() {
this.categoryItem.classList.toggle("blocked", this.enabled);
// Can't get `this.categoryItem` without the popup. Using the popup instead
// of `this.categoryItem` to guard access, because the category item getter
// can trigger bug 1543537. If there's no popup, we'll be called again the
// first time the popup shows.
if (gProtectionsHandler._protectionsPopup) {
this.categoryItem.classList.toggle("blocked", this.enabled);
}
},
get subView() {
@ -287,10 +305,14 @@ var TrackingProtection = {
enabledInPrivateWindows: false,
get categoryItem() {
delete this.categoryItem;
return (this.categoryItem = document.getElementById(
let item = document.getElementById(
"protections-popup-category-tracking-protection"
));
);
if (item) {
delete this.categoryItem;
this.categoryItem = item;
}
return item;
},
get subView() {
@ -385,7 +407,9 @@ var TrackingProtection = {
this.enabledInPrivateWindows = Services.prefs.getBoolPref(
this.PREF_ENABLED_IN_PRIVATE_WINDOWS
);
this.categoryItem.classList.toggle("blocked", this.enabled);
if (this.categoryItem) {
this.categoryItem.classList.toggle("blocked", this.enabled);
}
},
isBlocking(state) {
@ -531,10 +555,12 @@ var ThirdPartyCookies = {
],
get categoryItem() {
delete this.categoryItem;
return (this.categoryItem = document.getElementById(
"protections-popup-category-cookies"
));
let item = document.getElementById("protections-popup-category-cookies");
if (item) {
delete this.categoryItem;
this.categoryItem = item;
}
return item;
},
get subView() {
@ -611,7 +637,6 @@ var ThirdPartyCookies = {
Ci.nsICookieService.BEHAVIOR_ACCEPT,
this.updateCategoryItem.bind(this)
);
this.updateCategoryItem();
},
get categoryLabel() {
@ -622,6 +647,13 @@ var ThirdPartyCookies = {
},
updateCategoryItem() {
// Can't get `this.categoryItem` without the popup. Using the popup instead
// of `this.categoryItem` to guard access, because the category item getter
// can trigger bug 1543537. If there's no popup, we'll be called again the
// first time the popup shows.
if (!gProtectionsHandler._protectionsPopup) {
return;
}
this.categoryItem.classList.toggle("blocked", this.enabled);
let label;
@ -1014,7 +1046,6 @@ var SocialTracking = {
false,
this.updateCategoryItem.bind(this)
);
this.updateCategoryItem();
},
get blockingEnabled() {
@ -1025,6 +1056,13 @@ var SocialTracking = {
},
updateCategoryItem() {
// Can't get `this.categoryItem` without the popup. Using the popup instead
// of `this.categoryItem` to guard access, because the category item getter
// can trigger bug 1543537. If there's no popup, we'll be called again the
// first time the popup shows.
if (!gProtectionsHandler._protectionsPopup) {
return;
}
if (this.enabled) {
this.categoryItem.removeAttribute("uidisabled");
} else {
@ -1064,10 +1102,14 @@ var SocialTracking = {
},
get categoryItem() {
delete this.categoryItem;
return (this.categoryItem = document.getElementById(
let item = document.getElementById(
"protections-popup-category-socialblock"
));
);
if (item) {
delete this.categoryItem;
this.categoryItem = item;
}
return item;
},
get subView() {
@ -1139,13 +1181,35 @@ var gProtectionsHandler = {
PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url",
PREF_CB_CATEGORY: "browser.contentblocking.category",
// smart getters
get _protectionsPopup() {
delete this._protectionsPopup;
return (this._protectionsPopup = document.getElementById(
"protections-popup"
));
_protectionsPopup: null,
_initializePopup() {
if (!this._protectionsPopup) {
let wrapper = document.getElementById("template-protections-popup");
this._protectionsPopup = wrapper.content.firstElementChild;
wrapper.replaceWith(wrapper.content);
this.maybeSetMilestoneCounterText();
for (let blocker of this.blockers) {
if (blocker.updateCategoryItem) {
blocker.updateCategoryItem();
}
}
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
document.getElementById(
"protections-popup-sendReportView-learn-more"
).href = baseURL + "blocking-breakage";
}
},
_hidePopup() {
if (this._protectionsPopup) {
PanelMultiView.hidePopup(this._protectionsPopup);
}
},
// smart getters
get iconBox() {
delete this.iconBox;
return (this.iconBox = document.getElementById(
@ -1401,19 +1465,12 @@ var gProtectionsHandler = {
() => this.maybeSetMilestoneCounterText()
);
this.maybeSetMilestoneCounterText();
for (let blocker of this.blockers) {
if (blocker.init) {
blocker.init();
}
}
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
document.getElementById(
"protections-popup-sendReportView-learn-more"
).href = baseURL + "blocking-breakage";
// Add an observer to observe that the history has been cleared.
Services.obs.addObserver(this, "browser:purge-session-history");
},
@ -1587,6 +1644,9 @@ var gProtectionsHandler = {
}
this._updatingFooter = true;
// Take the popup out of its template.
this._initializePopup();
// Get the tracker count and set it to the counter in the footer.
const trackerCount = await TrackingDBService.sumAllEvents();
this.setTrackersBlockedCounter(trackerCount);
@ -1637,7 +1697,9 @@ var gProtectionsHandler = {
gBrowser.selectedBrowser
);
this._protectionsPopup.toggleAttribute("hasException", this.hasException);
if (this._protectionsPopup) {
this._protectionsPopup.toggleAttribute("hasException", this.hasException);
}
this.iconBox.toggleAttribute("hasException", this.hasException);
// Add to telemetry per page load as a baseline measurement.
@ -1683,72 +1745,34 @@ var gProtectionsHandler = {
);
},
onContentBlockingEvent(event, webProgress, isSimulated, previousState) {
// 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;
}
this.anyDetected = false;
this.anyBlocking = false;
this.noTrackersDetectedDescription.hidden = false;
/**
* Update the in-panel UI given a blocking event. Called when the popup
* is being shown, or when the popup is open while a new event comes in.
*/
updatePanelForBlockingEvent(event, isShown) {
// Update the categories:
for (let blocker of this.blockers) {
if (blocker.categoryItem.hasAttribute("uidisabled")) {
continue;
}
// 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);
let detected = blocker.isDetected(event);
blocker.categoryItem.classList.toggle("notFound", !detected);
this.anyDetected = this.anyDetected || detected;
this.anyBlocking = this.anyBlocking || blocker.activated;
blocker.categoryItem.classList.toggle(
"notFound",
!blocker.isDetected(event)
);
}
// Check whether the user has added an exception for this site.
this.hasException = ContentBlockingAllowList.includes(
gBrowser.selectedBrowser
);
// And the popup attributes:
this._protectionsPopup.toggleAttribute("detected", this.anyDetected);
this._protectionsPopup.toggleAttribute("blocking", this.anyBlocking);
this._protectionsPopup.toggleAttribute("hasException", this.hasException);
// 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 || !this.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 (this.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.
let isPanelOpen = ["showing", "open"].includes(
this._protectionsPopup.state
);
if (isPanelOpen) {
this._protectionsPopup.toggleAttribute("detected", this.anyDetected);
this._protectionsPopup.toggleAttribute("blocking", this.anyBlocking);
this._protectionsPopup.toggleAttribute("hasException", this.hasException);
}
this._categoryItemOrderInvalidated = true;
this.noTrackersDetectedDescription.hidden = this.anyDetected;
if (this.anyDetected) {
this.noTrackersDetectedDescription.hidden = true;
if (isPanelOpen) {
this.reorderCategoryItems();
// Reorder categories if any are in use.
this.reorderCategoryItems();
if (isShown) {
// Until we encounter a site that triggers them, category elements might
// be invisible when descriptionHeightWorkaround gets called, i.e. they
// are omitted from the workaround and the content overflows the panel.
@ -1758,31 +1782,21 @@ var gProtectionsHandler = {
).descriptionHeightWorkaround();
}
}
},
this.iconBox.toggleAttribute("active", this.anyBlocking);
this.iconBox.toggleAttribute("hasException", this.hasException);
if (this.hasException) {
this.showDisabledTooltipForTPIcon();
if (!this.hadShieldState && !isSimulated) {
reportBlockingEventTelemetry(event, isSimulated, previousState) {
if (!isSimulated) {
if (this.hasException && !this.hadShieldState) {
this.hadShieldState = true;
this.shieldHistogramAdd(1);
}
} else if (this.anyBlocking) {
this.showActiveTooltipForTPIcon();
if (!this.hadShieldState && !isSimulated) {
} else if (
!this.hasException &&
this.anyBlocking &&
!this.hadShieldState
) {
this.hadShieldState = true;
this.shieldHistogramAdd(2);
}
} else {
this.showNoTrackerTooltipForTPIcon();
}
// Don't send a content blocking event to CFR for
// tab switches since this will already be done via
// onStateChange.
if (!isSimulated) {
this.notifyContentBlockingEvent(event);
}
// We report up to one instance of fingerprinting and cryptomining
@ -1810,6 +1824,94 @@ var gProtectionsHandler = {
this.cryptominersHistogramAdd("allowed");
}
},
onContentBlockingEvent(event, webProgress, isSimulated, previousState) {
// 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;
}
// First update all our internal state based on the allowlist and the
// different blockers:
this.anyDetected = false;
this.anyBlocking = false;
this._lastEvent = event;
// Check whether the user has added an exception for this site.
this.hasException = ContentBlockingAllowList.includes(
gBrowser.selectedBrowser
);
// Update blocker state and find if they detected or blocked anything.
for (let blocker of this.blockers) {
if (blocker.categoryItem?.hasAttribute("uidisabled")) {
continue;
}
// Store data on whether the blocker is activated 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);
this.anyDetected = this.anyDetected || blocker.isDetected(event);
this.anyBlocking = this.anyBlocking || blocker.activated;
}
this._categoryItemOrderInvalidated = true;
// Now, update the icon UI:
// 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 || !this.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 (this.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.iconBox.toggleAttribute("active", this.anyBlocking);
this.iconBox.toggleAttribute("hasException", this.hasException);
// Update the icon's tooltip:
if (this.hasException) {
this.showDisabledTooltipForTPIcon();
} else if (this.anyBlocking) {
this.showActiveTooltipForTPIcon();
} else {
this.showNoTrackerTooltipForTPIcon();
}
// Update the panel if it's open.
let isPanelOpen = ["showing", "open"].includes(
this._protectionsPopup?.state
);
if (isPanelOpen) {
this.updatePanelForBlockingEvent(event, true);
}
// Notify other consumers, like CFR.
// Don't send a content blocking event to CFR for
// tab switches since this will already be done via
// onStateChange.
if (!isSimulated) {
this.notifyContentBlockingEvent(event);
}
// Finally, report telemetry.
this.reportBlockingEventTelemetry(event, isSimulated, previousState);
},
// We handle focus here when the panel is shown.
handleEvent(event) {
let elem = document.activeElement;
let position = elem.compareDocumentPosition(this._protectionsPopup);
@ -1839,6 +1941,10 @@ var gProtectionsHandler = {
}
},
/**
* Update the popup contents. Only called when the popup has been taken
* out of the template and is shown or about to be shown.
*/
refreshProtectionsPopup() {
let host = gIdentityHandler.getHostForDisplay();
@ -1977,7 +2083,7 @@ var gProtectionsHandler = {
disableForCurrentPage(shouldReload = true) {
ContentBlockingAllowList.add(gBrowser.selectedBrowser);
if (shouldReload) {
PanelMultiView.hidePopup(this._protectionsPopup);
this._hidePopup();
BrowserReload();
}
},
@ -1985,7 +2091,7 @@ var gProtectionsHandler = {
enableForCurrentPage(shouldReload = true) {
ContentBlockingAllowList.remove(gBrowser.selectedBrowser);
if (shouldReload) {
PanelMultiView.hidePopup(this._protectionsPopup);
this._hidePopup();
BrowserReload();
}
},
@ -2086,6 +2192,9 @@ var gProtectionsHandler = {
// refreshProtectionsPopup.
_milestoneTextSet: false,
async maybeSetMilestoneCounterText() {
if (!this._protectionsPopup) {
return;
}
let trackerCount = this.milestonePref;
if (
!this.milestonesEnabledPref ||
@ -2157,6 +2266,14 @@ var gProtectionsHandler = {
showProtectionsPopup(options = {}) {
const { event, toast } = options;
this._initializePopup();
// Ensure we've updated category state based on the last blocking event:
if (this.hasOwnProperty("_lastEvent")) {
this.updatePanelForBlockingEvent(this._lastEvent);
delete this._lastEvent;
}
// We need to clear the toast timer if it exists before showing the
// protections popup.
if (this._toastPanelTimer) {
@ -2164,10 +2281,6 @@ var gProtectionsHandler = {
delete this._toastPanelTimer;
}
// Make sure that the display:none style we set in xul is removed now that
// the popup is actually needed
this._protectionsPopup.hidden = false;
this._protectionsPopup.toggleAttribute("toast", !!toast);
if (!toast) {
// Refresh strings if we want to open it as a standard protections popup.
@ -2353,7 +2466,9 @@ var gProtectionsHandler = {
},
async maybeUpdateEarliestRecordedDateTooltip() {
if (this._hasEarliestRecord) {
// If we've already updated or the popup isn't in the DOM yet, don't bother
// doing this:
if (this._hasEarliestRecord || !this._protectionsPopup) {
return;
}

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

@ -122,7 +122,7 @@ add_task(async function testToggleSwitch() {
"TP Switch should be enabled"
);
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
@ -150,11 +150,11 @@ add_task(async function testToggleSwitch() {
// the popup shown event if we open the protections panel while the toast is
// opening.
let popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popupshown"
);
popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
@ -235,7 +235,7 @@ add_task(async function testSettingsButton() {
await openProtectionsPanel();
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
let newTabPromise = BrowserTestUtils.waitForNewTab(
@ -313,7 +313,7 @@ add_task(async function testShowFullReportButton() {
await openProtectionsPanel();
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
let newTabPromise = waitForAboutProtectionsTab();
@ -349,12 +349,13 @@ add_task(async function testMiniPanel() {
// Open the mini panel.
await openProtectionsPanel(true);
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
// Check that only the header is displayed.
for (let item of protectionsPopupMainView.childNodes) {
let mainView = document.getElementById("protections-popup-mainView");
for (let item of mainView.childNodes) {
if (item.id !== "protections-popup-mainView-panel-header-section") {
ok(
!BrowserTestUtils.is_visible(item),
@ -388,11 +389,11 @@ add_task(async function testToggleSwitchFlow() {
await openProtectionsPanel();
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
let popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popupshown"
);
let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
@ -418,14 +419,14 @@ add_task(async function testToggleSwitchFlow() {
// Click on the mini panel and making sure the protection popup shows up.
popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popupshown"
);
popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
protectionsPopupHeader.click();
document.getElementById("protections-popup-mainView-panel-header").click();
await popuphiddenPromise;
await popupShownPromise;
@ -436,11 +437,11 @@ add_task(async function testToggleSwitchFlow() {
// Click the TP switch again, from Off -> On.
popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popupshown"
);
browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
@ -455,7 +456,7 @@ add_task(async function testToggleSwitchFlow() {
// Protections popup hidden -> Page refresh -> Mini panel shows up.
await popuphiddenPromise;
popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
gProtectionsHandler._protectionsPopup,
"popuphidden"
);
await browserLoadedPromise;
@ -626,8 +627,12 @@ add_task(async function testSubViewTelemetry() {
for (let [item, telemetryId] of items) {
await BrowserTestUtils.withNewTab("http://www.example.com", async () => {
item.classList.remove("notFound"); // Force visible for test
await openProtectionsPanel();
item.classList.remove("notFound"); // Force visible for test
gProtectionsHandler._categoryItemOrderInvalidated = true;
gProtectionsHandler.reorderCategoryItems();
let viewShownEvent = BrowserTestUtils.waitForEvent(
gProtectionsHandler._protectionsPopupMultiView,
"ViewShown"

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

@ -25,6 +25,9 @@ registerCleanupFunction(function() {
add_task(async function testCookieCategoryLabel() {
await BrowserTestUtils.withNewTab("http://www.example.com", async function() {
// Ensure the category nodes exist.
await openProtectionsPanel();
await closeProtectionsPanel();
let categoryItem = document.getElementById(
"protections-popup-category-cookies"
);
@ -142,14 +145,6 @@ add_task(async function testCookieCategoryLabel() {
});
});
let categoryItems = [
"protections-popup-category-tracking-protection",
"protections-popup-category-socialblock",
"protections-popup-category-cookies",
"protections-popup-category-cryptominers",
"protections-popup-category-fingerprinters",
].map(id => document.getElementById(id));
let categoryEnabledPrefs = [TP_PREF, STC_PREF, TPC_PREF, CM_PREF, FP_PREF];
let detectedStateFlags = [
@ -183,6 +178,18 @@ add_task(async function testCategorySections() {
}
await BrowserTestUtils.withNewTab("http://www.example.com", async function() {
// Ensure the category nodes exist.
await openProtectionsPanel();
await closeProtectionsPanel();
let categoryItems = [
"protections-popup-category-tracking-protection",
"protections-popup-category-socialblock",
"protections-popup-category-cookies",
"protections-popup-category-cryptominers",
"protections-popup-category-fingerprinters",
].map(id => document.getElementById(id));
for (let item of categoryItems) {
await waitForClass(item, "notFound");
await waitForClass(item, "blocked", false);
@ -205,6 +212,7 @@ add_task(async function testCategorySections() {
Services.prefs.setBoolPref(enabledPref, true);
}
gProtectionsHandler.onContentBlockingEvent(contentBlockingState);
gProtectionsHandler.updatePanelForBlockingEvent(contentBlockingState);
await waitForClass(itemToTest, "notFound", false);
await waitForClass(itemToTest, "blocked", true);
if (enabledPref == TPC_PREF) {

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

@ -200,6 +200,8 @@ async function testCategoryItem() {
});
let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]);
await openProtectionsPanel();
let categoryItem = document.getElementById(
"protections-popup-category-cryptominers"
);
@ -227,6 +229,7 @@ async function testCategoryItem() {
categoryItem.classList.contains("notFound"),
"Category marked as not found"
);
await closeProtectionsPanel();
promise = waitForContentBlockingEvent();
@ -236,6 +239,7 @@ async function testCategoryItem() {
await promise;
await openProtectionsPanel();
ok(
!categoryItem.classList.contains("blocked"),
"Category not marked as blocked"
@ -259,6 +263,7 @@ async function testCategoryItem() {
!categoryItem.classList.contains("notFound"),
"Category not marked as not found"
);
await closeProtectionsPanel();
BrowserTestUtils.removeTab(tab);
}

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

@ -109,6 +109,7 @@ async function testCategoryItem() {
});
let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]);
await openProtectionsPanel();
let categoryItem = document.getElementById(
"protections-popup-category-fingerprinters"
);
@ -136,6 +137,7 @@ async function testCategoryItem() {
categoryItem.classList.contains("notFound"),
"Category marked as not found"
);
await closeProtectionsPanel();
promise = waitForContentBlockingEvent();
@ -145,6 +147,7 @@ async function testCategoryItem() {
await promise;
await openProtectionsPanel();
ok(
!categoryItem.classList.contains("blocked"),
"Category not marked as blocked"
@ -168,6 +171,7 @@ async function testCategoryItem() {
!categoryItem.classList.contains("notFound"),
"Category not marked as not found"
);
await closeProtectionsPanel();
BrowserTestUtils.removeTab(tab);
}

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

@ -28,7 +28,7 @@ function hidden(sel) {
function protectionsPopupState() {
let win = browser.ownerGlobal;
return win.document.getElementById("protections-popup").state;
return win.document.getElementById("protections-popup")?.state || "closed";
}
function clickButton(sel) {

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

@ -64,10 +64,10 @@ add_task(async function testReportBreakageCancel() {
Services.prefs.setBoolPref(TP_PREF, true);
await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function() {
await openProtectionsPanel();
await TestUtils.waitForCondition(() =>
gProtectionsHandler._protectionsPopup.hasAttribute("blocking")
);
await openProtectionsPanel();
let siteNotWorkingButton = document.getElementById(
"protections-popup-tp-switch-breakage-link"

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

@ -47,6 +47,7 @@ async function testIdentityState(hasException) {
await loaded;
}
await openProtectionsPanel();
let categoryItem = document.getElementById(
"protections-popup-category-socialblock"
);
@ -60,10 +61,12 @@ async function testIdentityState(hasException) {
BrowserTestUtils.is_visible(gProtectionsHandler.iconBox),
"icon box is visible regardless the exception"
);
await closeProtectionsPanel();
await SpecialPowers.spawn(tab.linkedBrowser, [], function() {
content.postMessage("socialtracking", "*");
});
await openProtectionsPanel();
await TestUtils.waitForCondition(() => {
return !categoryItem.classList.contains("notFound");
@ -86,6 +89,7 @@ async function testIdentityState(hasException) {
hasException,
"Shows an exception when appropriate"
);
await closeProtectionsPanel();
if (hasException) {
let loaded = BrowserTestUtils.browserLoaded(
@ -236,7 +240,7 @@ async function testCategoryItem(blockLoads) {
);
ok(
BrowserTestUtils.is_visible(noTrackersDetectedDesc),
"No Trackers Detcted should be shown"
"No Trackers detected should be shown"
);
BrowserTestUtils.removeTab(tab);
@ -262,7 +266,7 @@ async function testCategoryItem(blockLoads) {
ok(!BrowserTestUtils.is_visible(categoryItem), "Item should not be visible");
ok(
BrowserTestUtils.is_visible(noTrackersDetectedDesc),
"No Trackers Detcted should be shown"
"No Trackers detected should be shown"
);
ok(
!gProtectionsHandler._protectionsPopup.hasAttribute("detected"),
@ -285,7 +289,7 @@ async function testCategoryItem(blockLoads) {
ok(BrowserTestUtils.is_visible(categoryItem), "Item should be visible");
ok(
!BrowserTestUtils.is_visible(noTrackersDetectedDesc),
"No Trackers Detcted should be hidden"
"No Trackers detected should be hidden"
);
ok(
gProtectionsHandler._protectionsPopup.hasAttribute("detected"),

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

@ -58,7 +58,7 @@ function notFound(id) {
return doc.getElementById(id).classList.contains("notFound");
}
function testBenignPage() {
async function testBenignPage() {
info("Non-tracking content must not be blocked");
ok(!gProtectionsHandler.anyDetected, "no trackers are detected");
ok(!gProtectionsHandler.hasException, "content shows no exception");
@ -80,6 +80,9 @@ function testBenignPage() {
BrowserTestUtils.is_visible(gProtectionsHandler.iconBox),
"icon box is visible"
);
let win = tabbrowser.ownerGlobal;
await openProtectionsPanel(false, win);
ok(
notFound("protections-popup-category-cookies"),
"Cookie restrictions category is not found"
@ -88,9 +91,10 @@ function testBenignPage() {
notFound("protections-popup-category-tracking-protection"),
"Trackers category is not found"
);
await closeProtectionsPanel(win);
}
function testBenignPageWithException() {
async function testBenignPageWithException() {
info("Non-tracking content must not be blocked");
ok(!gProtectionsHandler.anyDetected, "no trackers are detected");
ok(gProtectionsHandler.hasException, "content shows exception");
@ -114,6 +118,8 @@ function testBenignPageWithException() {
"icon box is not hidden"
);
let win = tabbrowser.ownerGlobal;
await openProtectionsPanel(false, win);
ok(
notFound("protections-popup-category-cookies"),
"Cookie restrictions category is not found"
@ -122,6 +128,7 @@ function testBenignPageWithException() {
notFound("protections-popup-category-tracking-protection"),
"Trackers category is not found"
);
await closeProtectionsPanel(win);
}
function areTrackersBlocked(isPrivateBrowsing) {
@ -135,7 +142,7 @@ function areTrackersBlocked(isPrivateBrowsing) {
return blockedByTP || blockedByTPC;
}
function testTrackingPage(window) {
async function testTrackingPage(window) {
info("Tracking content must be blocked");
ok(gProtectionsHandler.anyDetected, "trackers are detected");
ok(!gProtectionsHandler.hasException, "content shows no exception");
@ -161,6 +168,7 @@ function testTrackingPage(window) {
"correct tooltip"
);
await openProtectionsPanel(false, window);
ok(
!notFound("protections-popup-category-tracking-protection"),
"Trackers category is detected"
@ -176,10 +184,11 @@ function testTrackingPage(window) {
"Cookie restrictions category is not found"
);
}
await closeProtectionsPanel(window);
}
function testTrackingPageUnblocked(blockedByTP, window) {
info("Tracking content must be white-listed and not blocked");
async function testTrackingPageUnblocked(blockedByTP, window) {
info("Tracking content must be in the exception list and not blocked");
ok(gProtectionsHandler.anyDetected, "trackers are detected");
ok(gProtectionsHandler.hasException, "content shows exception");
@ -202,6 +211,7 @@ function testTrackingPageUnblocked(blockedByTP, window) {
"icon box is visible"
);
await openProtectionsPanel(false, window);
ok(
!notFound("protections-popup-category-tracking-protection"),
"Trackers category is detected"
@ -217,6 +227,7 @@ function testTrackingPageUnblocked(blockedByTP, window) {
"Cookie restrictions category is not found"
);
}
await closeProtectionsPanel(window);
}
async function testContentBlocking(tab) {
@ -224,7 +235,7 @@ async function testContentBlocking(tab) {
info("Load a test page not containing tracking elements");
await promiseTabLoadEvent(tab, BENIGN_PAGE);
testBenignPage();
await testBenignPage();
info(
"Load a test page not containing tracking elements which has an exception."
@ -237,13 +248,13 @@ async function testContentBlocking(tab) {
// notification which would trigger an oncontentblocking notification for us.
await promiseTabLoadEvent(tab, "https://example.org/?round=2");
testBenignPageWithException();
await testBenignPageWithException();
ContentBlockingAllowList.remove(tab.linkedBrowser);
info("Load a test page containing tracking elements");
await promiseTabLoadEvent(tab, gTrackingPageURL);
testTrackingPage(tab.ownerGlobal);
await testTrackingPage(tab.ownerGlobal);
info("Disable CB for the page (which reloads the page)");
let tabReloadPromise = promiseTabLoadEvent(tab);
@ -251,13 +262,13 @@ async function testContentBlocking(tab) {
await tabReloadPromise;
let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(tab.ownerGlobal);
let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
testTrackingPageUnblocked(blockedByTP, tab.ownerGlobal);
await testTrackingPageUnblocked(blockedByTP, tab.ownerGlobal);
info("Re-enable TP for the page (which reloads the page)");
tabReloadPromise = promiseTabLoadEvent(tab);
tab.ownerGlobal.gProtectionsHandler.enableForCurrentPage();
await tabReloadPromise;
testTrackingPage(tab.ownerGlobal);
await testTrackingPage(tab.ownerGlobal);
}
add_task(async function testNormalBrowsing() {

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

@ -23,36 +23,39 @@ ChromeUtils.defineModuleGetter(
var { UrlClassifierTestUtils } = ChromeUtils.import(
"resource://testing-common/UrlClassifierTestUtils.jsm"
);
var protectionsPopup = document.getElementById("protections-popup");
var protectionsPopupMainView = document.getElementById(
"protections-popup-mainView"
);
var protectionsPopupHeader = document.getElementById(
"protections-popup-mainView-panel-header"
);
async function openProtectionsPanel(toast) {
async function openProtectionsPanel(toast, win = window) {
let popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
"popupshown"
win,
"popupshown",
true,
e => e.target.id == "protections-popup"
);
let shieldIconContainer = document.getElementById(
let shieldIconContainer = win.document.getElementById(
"tracking-protection-icon-container"
);
// Move out than move over the shield icon to trigger the hover event in
// order to fetch tracker count.
EventUtils.synthesizeMouseAtCenter(gURLBar.textbox, {
type: "mousemove",
});
EventUtils.synthesizeMouseAtCenter(shieldIconContainer, {
type: "mousemove",
});
EventUtils.synthesizeMouseAtCenter(
win.gURLBar.textbox,
{
type: "mousemove",
},
win
);
EventUtils.synthesizeMouseAtCenter(
shieldIconContainer,
{
type: "mousemove",
},
win
);
if (!toast) {
EventUtils.synthesizeMouseAtCenter(shieldIconContainer, {});
EventUtils.synthesizeMouseAtCenter(shieldIconContainer, {}, win);
} else {
gProtectionsHandler.showProtectionsPopup({ toast });
win.gProtectionsHandler.showProtectionsPopup({ toast });
}
await popupShownPromise;
@ -60,8 +63,10 @@ async function openProtectionsPanel(toast) {
async function openProtectionsPanelWithKeyNav() {
let popupShownPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
"popupshown"
window,
"popupshown",
true,
e => e.target.id == "protections-popup"
);
gURLBar.focus();
@ -74,7 +79,11 @@ async function openProtectionsPanelWithKeyNav() {
await popupShownPromise;
}
async function closeProtectionsPanel() {
async function closeProtectionsPanel(win = window) {
let protectionsPopup = win.document.getElementById("protections-popup");
if (!protectionsPopup) {
return;
}
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
protectionsPopup,
"popuphidden"

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

@ -2,14 +2,13 @@
- 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/. -->
<html:template id="template-protections-popup">
<panel id="protections-popup"
class="panel-no-padding"
type="arrow"
hidden="true"
role="alertdialog"
noautofocus="true"
aria-labelledby="protections-popup-main-header-label"
onpopupshowing="gProtectionsHandler.reorderCategoryItems()"
onpopupshown="gProtectionsHandler.onPopupShown(event);"
onpopuphidden="gProtectionsHandler.onPopupHidden(event);"
orient="vertical">
@ -325,3 +324,4 @@
</panelview>
</panelmultiview>
</panel>
</html:template>

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

@ -318,6 +318,8 @@ async function openProtectionsPopup() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let gBrowser = browserWindow.gBrowser;
let { gProtectionsHandler } = gBrowser.ownerGlobal;
// Force initializing the popup; we can't add classes otherwise.
gProtectionsHandler._initializePopup();
gProtectionsHandler._protectionsPopup.hidePopup();
// Disable the popup shadow on OSX until we have figured out bug 1425253.
if (AppConstants.platform == "macosx") {

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

@ -5,14 +5,18 @@
add_task(async function test_OPEN_PROTECTION_PANEL() {
await BrowserTestUtils.withNewTab(EXAMPLE_URL, async browser => {
const popupEl = document.getElementById("protections-popup");
const popupshown = BrowserTestUtils.waitForEvent(popupEl, "popupshown");
const popupshown = BrowserTestUtils.waitForEvent(
window,
"popupshown",
true,
e => e.target.id == "protections-popup"
);
await SMATestUtils.executeAndValidateAction({
type: "OPEN_PROTECTION_PANEL",
});
await popupshown;
let { target: popupEl } = await popupshown;
Assert.equal(popupEl.state, "open", "Protections popup is open.");
});
});