Bug 1477046 - Part 1: Add basic FastBlock automation tests; r=mayhemer

Basic parts of FastBlock tests, no iframe/xhr tests in this patch.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Liang-Heng Chen 2018-09-13 21:27:54 +00:00
Родитель 87e37e32a0
Коммит ac54284832
4 изменённых файлов: 355 добавлений и 1 удалений

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

@ -759,7 +759,7 @@ nsHttpChannel::ConnectOnTailUnblock()
if (isTrackingResource && CheckFastBlocked()) {
AntiTrackingCommon::NotifyRejection(this,
nsIWebProgressListener::STATE_BLOCKED_SLOW_TRACKING_CONTENT);
Unused << AsyncAbort(NS_ERROR_ABORT);
Unused << AsyncAbort(NS_ERROR_TRACKING_ANNOTATION_URI);
CloseCacheEntry(false);
return NS_OK;
}

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

@ -12,6 +12,7 @@ support-files =
classifierHelper.js
head.js
threathit.sjs
fastblock.html
!/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
!/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
!/toolkit/components/url-classifier/tests/mochitest/good.js
@ -65,3 +66,4 @@ skip-if = verify
[test_advisory_link.html]
[test_threathit_report.html]
skip-if = verify
[test_fastblock_bug1477046.html]

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

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<!-- Tracking URL -->
<script id="goodScript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
<!-- Tracking Annotation -->
<script id="fastScript" data-touched="not sure" src="http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
<!-- Tracking Annotation -->
<script id="slowScript" data-touched="not sure" src="http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js" onload="this.dataset.touched='yes';" onerror="this.dataset.touched='no';"></script>
</body>
</html>

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

@ -0,0 +1,334 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1477046
------------------------------------------
| ID | Tracker | FastBlock |
-------------------+---------+------------
| fastblock.html | No | N/A |
-------------------+---------+------------
| goodScript | No | N/A |
| fastScript | Yes | No |
| slowScript | Yes | Yes |
------------------------------------------
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1477046</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script src="head.js"></script>
<script class="testbody" type="text/javascript">
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
ChromeUtils.import("resource://testing-common/ContentTask.jsm");
ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm");
ChromeUtils.import("resource://testing-common/TestUtils.jsm");
ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
const gContentPage =
"http://example.com/chrome/toolkit/components/url-classifier/tests/mochitest/fastblock.html";
const gSlowTrackers = [
"http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js",
];
const gInfiniteTimeout = 300000;
const gDebug = false;
function log(aMsg) {
if (gDebug) {
info("[FastBlock] " + aMsg + "\n");
}
}
function awaitBrowserLoaded(browser) {
return ContentTask.spawn(browser, null, () => {
if (content.document.readyState !== "complete" ||
content.document.documentURI === "about:blank") {
return ContentTaskUtils.waitForEvent(this, "load", true, event => {
return content.document.documentURI !== "about:blank";
}).then(() => {
return ContentTaskUtils.waitForCondition(() => {
return content.document.readyState === "complete";
}, "page load complete");
});
}
return Promise.resolve();
});
}
function testOnWindow(aCallback) {
const mainWindow = window.docShell.rootTreeItem.domWindow;
let win = mainWindow.OpenBrowserWindow({remote: true});
whenDelayedStartupFinished(win, function() {
SimpleTest.executeSoon(function() { win.gBrowser.loadURI(gContentPage); });
awaitBrowserLoaded(win.gBrowser.selectedBrowser).then(() => {
aCallback(win);
});
});
}
async function setupTest() {
log("setupTest");
SimpleTest.waitForExplicitFinish();
SimpleTest.registerCleanupFunction(() => {
log("cleanup()");
UrlClassifierTestUtils.cleanupTestTrackers();
});
await SpecialPowers.pushPrefEnv({ "set": [
["browser.contentblocking.enabled", true],
["browser.fastblock.enabled", true],
["browser.fastblock.timeout", 5000],
["network.http.tailing.enabled", true],
["privacy.trackingprotection.enabled", false],
["privacy.trackingprotection.annotate_channels", true],
]});
await UrlClassifierTestUtils.addTestTrackers();
}
/**
* This is a helper function that detects slower trackers loaded before timeout.
* All such trackers will be suspended until other resources are loaded.
* At the moment, the timeout will be set to immediately, which makes all following
* trackers blocked by FastBlock. This test depends on the implementation in
* |nsHttpChannel|, where the timeout check is after resume.
*/
function SlowTrackerDetector(aNumNonSlowTargets) {
this.numTargets = aNumNonSlowTargets;
this.suspendedRequests = [];
this._onBC = this.onBeforeConnect.bind(this);
this._onSR = this.onStopRequest.bind(this);
}
SlowTrackerDetector.prototype = {
async init() {
SpecialPowers.addObserver(this._onBC, "http-on-before-connect");
SpecialPowers.addObserver(this._onSR, "http-on-stop-request");
await SpecialPowers.pushPrefEnv({
"set": [
["browser.fastblock.timeout", gInfiniteTimeout],
],
});
},
async close() {
ok(!this._onBC, "observer removed");
ok(!this._onSR, "observer removed");
is(this.suspendedRequests.length, 0, "no suspended request");
await SpecialPowers.popPrefEnv();
},
onBeforeConnect(aSubject) {
let channel = aSubject.QueryInterface(Ci.nsIChannel);
log("onBeforeConnect: " + channel.name);
if (gSlowTrackers.includes(channel.name) &&
this.numTargets > 0) {
this.onSlowTrackerFound(channel);
}
},
onStopRequest(aSubject) {
let channel = aSubject.QueryInterface(Ci.nsIChannel);
log("onStopRequest: " + channel.name);
if (!gSlowTrackers.includes(channel.name)) {
--this.numTargets;
if (this.numTargets == 0) {
this.onAllNonSlowTargetsLoaded();
}
}
},
onSlowTrackerFound(aChannel) {
log("onSlowTrackerFound: " + aChannel.name);
aChannel.suspend();
this.suspendedRequests.push(aChannel);
},
onAllNonSlowTargetsLoaded() {
is(this.numTargets, 0, "resources are loaded");
SpecialPowers.removeObserver(this._onBC, "http-on-before-connect");
SpecialPowers.removeObserver(this._onSR, "http-on-stop-request");
this._onBC = this._onSR = null;
log("=========== TIMEOUT ===========");
// FastBlock from now on
SpecialPowers.setIntPref("browser.fastblock.timeout", 1);
for (let channel of this.suspendedRequests) {
log("resuming: " + channel.name);
channel.resume();
}
this.suspendedRequests = [];
},
};
/**
* DOM will do some SpeculativeLoad thing, which increases the tracker counter
* incorrectly. This class is introduced to cancel those duplicated channels.
*/
function ChannelUnifier() {
this._onOR = this.onOpeningRequest.bind(this);
}
ChannelUnifier.prototype = {
init() {
this.uris = new Set();
SpecialPowers.addObserver(this._onOR, "http-on-opening-request");
},
close() {
SpecialPowers.removeObserver(this._onOR, "http-on-opening-request");
},
onOpeningRequest(aSubject) {
let channel = aSubject.QueryInterface(Ci.nsIChannel);
if (this.uris.has(channel.name)) {
log("ChannelUnifier cancels: " + channel.name);
channel.cancel(Cr.NS_BINDING_ABORTED);
} else {
this.uris.add(channel.name);
}
},
};
async function runTest(aPrefs, aFunction, aNumNonSlowTrackers) {
if (aPrefs) {
await SpecialPowers.pushPrefEnv(aPrefs);
}
let detector;
if (aNumNonSlowTrackers) {
detector = new SlowTrackerDetector(aNumNonSlowTrackers);
await detector.init();
}
await new Promise(resolve => {
let monitor = new ChannelUnifier();
monitor.init();
testOnWindow(async function(aWindow) {
monitor.close();
await aFunction(aWindow);
await BrowserTestUtils.closeWindow(aWindow);
if (detector) {
await detector.close();
detector = null;
}
if (aPrefs) {
await SpecialPowers.popPrefEnv();
}
resolve();
});
});
}
async function finishTest() {
log("finishTest()");
SimpleTest.finish();
}
async function testFastBlock(aWindow) {
let browser = aWindow.gBrowser.selectedBrowser;
let results = await ContentTask.spawn(browser, {}, () => {
return {
goodScript: content.document.getElementById("goodScript").dataset.touched,
fastScript: content.document.getElementById("fastScript").dataset.touched,
slowScript: content.document.getElementById("slowScript").dataset.touched,
numTrackersFound: content.document.numTrackersFound,
numTrackersBlocked: content.document.numTrackersBlocked,
};
});
let { goodScript, fastScript, slowScript, numTrackersFound, numTrackersBlocked } = results;
is(goodScript, "yes", "is not a tracker");
is(fastScript, "yes", "is a tracker before timeout");
is(slowScript, "no", "is a blocked tracker");
is(numTrackersFound, 2, "2 trackers found");
is(numTrackersBlocked, 1, "1 tracker blocked");
}
async function testNoFastBlock(aWindow) {
let browser = aWindow.gBrowser.selectedBrowser;
let results = await ContentTask.spawn(browser, {}, () => {
return {
goodScript: content.document.getElementById("goodScript").dataset.touched,
fastScript: content.document.getElementById("fastScript").dataset.touched,
slowScript: content.document.getElementById("slowScript").dataset.touched,
numTrackersFound: content.document.numTrackersFound,
numTrackersBlocked: content.document.numTrackersBlocked,
};
});
let { goodScript, fastScript, slowScript, numTrackersFound, numTrackersBlocked } = results;
is(goodScript, "yes", "is not a tracker");
is(fastScript, "yes", "is a non-blocked tracker");
is(slowScript, "yes", "is a non-blocked tracker");
is(numTrackersFound, 2, "2 trackers found");
is(numTrackersBlocked, 0, "no tracker blocked");
}
async function testPrefsSwitch() {
// FastBlock ON
await runTest({
"set": [
["browser.contentblocking.enabled", true],
["browser.fastblock.enabled", true],
]}, testFastBlock, 3); // // fastblock.html, good.js, evil.js
// FastBlock OFF
await runTest({
"set": [
["browser.contentblocking.enabled", false],
["browser.fastblock.enabled", true],
["browser.fastblock.timeout", 1],
]}, testNoFastBlock, 0);
// FastBlock OFF
await runTest({
"set": [
["browser.contentblocking.enabled", true],
["browser.fastblock.enabled", false],
["browser.fastblock.timeout", 1],
]}, testNoFastBlock, 0);
}
async function test() {
await setupTest();
await runTest(null, testFastBlock, 3); // fastblock.html, good.js, evil.js
// A long-timeout that FastBlock never happens
await runTest({ "set": [["browser.fastblock.timeout", gInfiniteTimeout]]},
testNoFastBlock, 0);
await testPrefsSwitch();
await finishTest();
}
test();
</script>
</pre>
</body>
</html>