Bug 1522630 - Report content blocking status notifications from the idle event queue; r=baku

Differential Revision: https://phabricator.services.mozilla.com/D17554
This commit is contained in:
Ehsan Akhgari 2019-01-24 16:21:26 -05:00
parent b48100112a
commit 5591361c34
7 changed files with 154 additions and 97 deletions

View File

@ -7,12 +7,14 @@ const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TP_PREF = "privacy.trackingprotection.enabled";
const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
// Test that the shield icon animation can be controlled by the cosmetic
// animations pref and that one of the icons is visible in each case.
add_task(async function testShieldAnimation() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(TP_PREF, true);
Services.prefs.setBoolPref(DTSCBN_PREF, true);
let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
@ -20,7 +22,8 @@ add_task(async function testShieldAnimation() {
let noAnimationIcon = document.getElementById("tracking-protection-icon");
Services.prefs.setBoolPref(ANIMATIONS_PREF, true);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden when animations are enabled");
ok(BrowserTestUtils.is_visible(animationIcon), "the animated icon is shown when animations are enabled");
@ -29,12 +32,14 @@ add_task(async function testShieldAnimation() {
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
Services.prefs.setBoolPref(ANIMATIONS_PREF, false);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown when animations are disabled");
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden when animations are disabled");
gBrowser.removeCurrentTab();
Services.prefs.clearUserPref(ANIMATIONS_PREF);
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
UrlClassifierTestUtils.cleanupTestTrackers();
});

View File

@ -11,6 +11,7 @@ const NCB_PREF = "network.cookie.cookieBehavior";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
const COOKIE_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/cookiePage.html";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
requestLongerTimeout(2);
@ -19,10 +20,13 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(NCB_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
Services.prefs.clearUserPref(ContentBlocking.prefIntroCount);
});
async function testTrackingProtectionAnimation(tabbrowser) {
Services.prefs.setBoolPref(DTSCBN_PREF, true);
info("Load a test page not containing tracking elements");
let benignTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE);
let ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking;

View File

@ -6,12 +6,14 @@
const TP_PB_PREF = "privacy.trackingprotection.enabled";
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 ContentBlocking = null;
var browser = null;
registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
ContentBlocking = TrackingProtection = browser = null;
UrlClassifierTestUtils.cleanupTestTrackers();
});
@ -79,6 +81,7 @@ function testTrackingPageUnblocked() {
add_task(async function testExceptionAddition() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(DTSCBN_PREF, true);
let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
browser = privateWin.gBrowser;
let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
@ -92,7 +95,8 @@ add_task(async function testExceptionAddition() {
ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
info("Load a test page containing tracking elements");
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
testTrackingPage(tab.ownerGlobal);
@ -128,7 +132,8 @@ add_task(async function testExceptionPersistence() {
ok(TrackingProtection.enabled, "TP is still enabled");
info("Load a test page containing tracking elements");
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
testTrackingPage(tab.ownerGlobal);
@ -137,7 +142,8 @@ add_task(async function testExceptionPersistence() {
clickButton("#tracking-action-unblock");
is(identityPopupState(), "closed", "Identity popup is closed");
await tabReloadPromise;
await Promise.all([tabReloadPromise,
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
testTrackingPageUnblocked();
privateWin.close();

View File

@ -17,6 +17,7 @@
const TP_PREF = "privacy.trackingprotection.enabled";
const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
const TPC_PREF = "network.cookie.cookieBehavior";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
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";
@ -33,6 +34,7 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(TPC_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
});
// This is a special version of "hidden" that doesn't check for item
@ -106,8 +108,8 @@ function testTrackingPage(window) {
ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
ok(!ContentBlocking.content.hasAttribute("hasException"), "content shows no exception");
let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);
let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
let blockedByTP = areTrackersBlocked(isWindowPrivate);
is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockedByTP,
"icon box is" + (blockedByTP ? "" : " not") + " visible");
is(ContentBlocking.iconBox.hasAttribute("active"), blockedByTP,
@ -119,7 +121,6 @@ function testTrackingPage(window) {
ok(hidden("#tracking-action-block"), "blockButton is hidden");
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
if (isWindowPrivate) {
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
is(!hidden("#tracking-action-unblock-private"), blockedByTP,
@ -218,6 +219,8 @@ async function testContentBlocking(tab) {
add_task(async function testNormalBrowsing() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(DTSCBN_PREF, true);
tabbrowser = gBrowser;
let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);

View File

@ -8,6 +8,7 @@ const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/te
const TP_PREF = "privacy.trackingprotection.enabled";
const COOKIE_PREF = "network.cookie.cookieBehavior";
const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
// Check that the shield icon is always hidden when all content blocking
// categories are turned off, even when content blocking is on.
@ -15,6 +16,7 @@ add_task(async function testContentBlockingAllDisabled() {
await SpecialPowers.pushPrefEnv({set: [
[TP_PREF, false],
[COOKIE_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
[DTSCBN_PREF, true],
]});
await UrlClassifierTestUtils.addTestTrackers();
@ -30,7 +32,8 @@ add_task(async function testContentBlockingAllDisabled() {
let animationIcon = document.getElementById("tracking-protection-icon-animatable-image");
let noAnimationIcon = document.getElementById("tracking-protection-icon");
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
@ -47,6 +50,7 @@ add_task(async function testContentBlockingAllDisabled() {
await SpecialPowers.pushPrefEnv({set: [
[TP_PREF, true],
]});
await promiseTabLoadEvent(tab, TRACKING_PAGE);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown");
});

View File

@ -319,6 +319,8 @@ static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
static int32_t gOpenPopupSpamCount = 0;
static bool gSyncContentBlockingNotifications = false;
nsGlobalWindowOuter::OuterWindowByIdTable*
nsGlobalWindowOuter::sOuterWindowsById = nullptr;
@ -5405,100 +5407,130 @@ void nsGlobalWindowOuter::NotifyContentBlockingEvent(unsigned aEvent,
nsCOMPtr<Document> doc = docShell->GetDocument();
NS_ENSURE_TRUE_VOID(doc);
// This event might come after the user has navigated to another page.
// To prevent showing the TrackingProtection UI on the wrong page, we need to
// check that the loading URI for the channel is the same as the URI currently
// loaded in the document.
if (!SameLoadingURI(doc, aChannel) &&
aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
return;
nsCOMPtr<nsIURI> uri(aURIHint);
nsCOMPtr<nsIChannel> channel(aChannel);
static bool prefInitialized = false;
if (!prefInitialized) {
Preferences::AddBoolVarCache(
&gSyncContentBlockingNotifications,
"dom.testing.sync-content-blocking-notifications", false);
prefInitialized = true;
}
// Notify nsIWebProgressListeners of this content blocking event.
// Can be used to change the UI state.
nsresult rv = NS_OK;
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t event = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return;
}
securityUI->GetContentBlockingEvent(&event);
nsAutoCString origin;
nsContentUtils::GetASCIIOrigin(aURIHint, origin);
nsCOMPtr<nsIRunnable> func = NS_NewRunnableFunction(
"NotifyContentBlockingEventDelayed",
[doc, docShell, uri, channel, aEvent, aBlocked]() {
// This event might come after the user has navigated to another
// page. To prevent showing the TrackingProtection UI on the wrong
// page, we need to check that the loading URI for the channel is
// the same as the URI currently loaded in the document.
if (!SameLoadingURI(doc, channel) &&
aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
return;
}
bool blockedValue = aBlocked;
bool unblocked = false;
if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
doc->SetHasTrackingContentBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
doc->SetHasTrackingContentLoaded(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentLoaded();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesBlockedByPermission();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
doc->SetHasAllCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasAllCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
doc->SetHasForeignCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasForeignCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
MOZ_ASSERT(!aBlocked,
"We don't expected to see blocked STATE_COOKIES_LOADED");
// Note that the logic in this branch is the logical negation of the logic
// in other branches, since the Document API we have is phrased in
// "loaded" terms as opposed to "blocked" terms.
blockedValue = !aBlocked;
doc->SetHasCookiesLoaded(blockedValue, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesLoaded();
}
} else {
// Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
}
const uint32_t oldEvent = event;
if (blockedValue) {
event |= aEvent;
} else if (unblocked) {
event &= ~aEvent;
}
// Notify nsIWebProgressListeners of this content blocking event.
// Can be used to change the UI state.
nsresult rv = NS_OK;
nsCOMPtr<nsISecurityEventSink> eventSink =
do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t event = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return;
}
securityUI->GetContentBlockingEvent(&event);
nsAutoCString origin;
nsContentUtils::GetASCIIOrigin(uri, origin);
if (event == oldEvent
bool blockedValue = aBlocked;
bool unblocked = false;
if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
doc->SetHasTrackingContentBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
doc->SetHasTrackingContentLoaded(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentLoaded();
}
} else if (aEvent == nsIWebProgressListener::
STATE_COOKIES_BLOCKED_BY_PERMISSION) {
doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesBlockedByPermission();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingCookiesBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
doc->SetHasAllCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasAllCookiesBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
doc->SetHasForeignCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasForeignCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
MOZ_ASSERT(!aBlocked,
"We don't expected to see blocked STATE_COOKIES_LOADED");
// Note that the logic in this branch is the logical negation of
// the logic in other branches, since the Document API we have is
// phrased in "loaded" terms as opposed to "blocked" terms.
blockedValue = !aBlocked;
doc->SetHasCookiesLoaded(blockedValue, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesLoaded();
}
} else {
// Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
}
const uint32_t oldEvent = event;
if (blockedValue) {
event |= aEvent;
} else if (unblocked) {
event &= ~aEvent;
}
if (event == oldEvent
#ifdef ANDROID
// GeckoView always needs to notify about blocked trackers, since the
// GeckoView API always needs to report the URI and type of any blocked
// tracker.
// We use a platform-dependent code path here because reporting this
// notification on desktop platforms isn't necessary and doing so can have
// a big performance cost.
&& aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
// GeckoView always needs to notify about blocked trackers,
// since the GeckoView API always needs to report the URI and
// type of any blocked tracker. We use a platform-dependent code
// path here because reporting this notification on desktop
// platforms isn't necessary and doing so can have a big
// performance cost.
&& aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
#endif
) {
// Avoid dispatching repeated notifications when nothing has changed
) {
// Avoid dispatching repeated notifications when nothing has
// changed
return;
}
eventSink->OnContentBlockingEvent(channel, event);
});
nsresult rv;
if (gSyncContentBlockingNotifications) {
rv = func->Run();
} else {
rv = NS_IdleDispatchToCurrentThread(func.forget(), 100);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
eventSink->OnContentBlockingEvent(aChannel, event);
}
// static

View File

@ -210,7 +210,10 @@ function testOnWindow(aTestData) {
});
}
SpecialPowers.pushPrefEnv(
{"set": [["browser.safebrowsing.phishing.enabled", true]]},
{"set": [
["browser.safebrowsing.phishing.enabled", true],
["dom.testing.sync-content-blocking-notifications", true],
]},
test);
function test() {