зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1843308 - Add tests for bounce tracking using popups and new tabs. r=bvandersloot,anti-tracking-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D206758
This commit is contained in:
Родитель
f4706ee481
Коммит
10905769fd
|
@ -16,6 +16,8 @@ support-files = [
|
|||
|
||||
["browser_bouncetracking_oa_isolation.js"]
|
||||
|
||||
["browser_bouncetracking_popup.js"]
|
||||
|
||||
["browser_bouncetracking_purge.js"]
|
||||
|
||||
["browser_bouncetracking_schemes.js"]
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { SiteDataTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/SiteDataTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const TEST_ORIGIN = "https://itisatracker.org";
|
||||
const TEST_BASE_DOMAIN = "itisatracker.org";
|
||||
|
||||
|
@ -28,6 +24,7 @@ async function runPurgeTest(expectPurge) {
|
|||
await runTestBounce({
|
||||
bounceType: "client",
|
||||
setState: "localStorage",
|
||||
skipSiteDataCleanup: true,
|
||||
postBounceCallback: () => {
|
||||
info(
|
||||
"Test that after the bounce but before purging cookies and localStorage are present."
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let bounceTrackingProtection;
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["privacy.bounceTrackingProtection.requireStatefulBounces", true],
|
||||
["privacy.bounceTrackingProtection.bounceTrackingGracePeriodSec", 0],
|
||||
],
|
||||
});
|
||||
bounceTrackingProtection = Cc[
|
||||
"@mozilla.org/bounce-tracking-protection;1"
|
||||
].getService(Ci.nsIBounceTrackingProtection);
|
||||
});
|
||||
|
||||
async function runTest(spawnWindowType) {
|
||||
if (!spawnWindowType || !["newTab", "popup"].includes(spawnWindowType)) {
|
||||
throw new Error(`Invalid option '${spawnWindowType}' for spawnWindowType`);
|
||||
}
|
||||
|
||||
Assert.equal(
|
||||
bounceTrackingProtection.testGetBounceTrackerCandidateHosts({}).length,
|
||||
0,
|
||||
"No bounce tracker hosts initially."
|
||||
);
|
||||
Assert.equal(
|
||||
bounceTrackingProtection.testGetUserActivationHosts({}).length,
|
||||
0,
|
||||
"No user activation hosts initially."
|
||||
);
|
||||
|
||||
// Spawn a tab with A, the start of the bounce chain.
|
||||
await BrowserTestUtils.withNewTab(
|
||||
getBaseUrl(ORIGIN_A) + "file_start.html",
|
||||
async browser => {
|
||||
// The destination site C to navigate to after the bounce.
|
||||
let finalURL = new URL(getBaseUrl(ORIGIN_B) + "file_start.html");
|
||||
// The middle hop in the bounce chain B that redirects to finalURL C.
|
||||
let bounceURL = getBounceURL({
|
||||
bounceType: "client",
|
||||
targetURL: finalURL,
|
||||
setState: "cookie-client",
|
||||
});
|
||||
|
||||
// Register a promise for the new popup window. This resolves once the popup
|
||||
// has opened and the final url (C) has been loaded.
|
||||
let openPromise;
|
||||
|
||||
if (spawnWindowType == "newTab") {
|
||||
openPromise = BrowserTestUtils.waitForNewTab(gBrowser, finalURL.href);
|
||||
} else {
|
||||
openPromise = BrowserTestUtils.waitForNewWindow({ url: finalURL.href });
|
||||
}
|
||||
|
||||
// Navigate through the bounce chain by opening a popup to the bounce URL.
|
||||
await navigateLinkClick(browser, bounceURL, {
|
||||
spawnWindow: spawnWindowType,
|
||||
});
|
||||
|
||||
let tabOrWindow = await openPromise;
|
||||
|
||||
let tabOrWindowBrowser;
|
||||
if (spawnWindowType == "newTab") {
|
||||
tabOrWindowBrowser = tabOrWindow.linkedBrowser;
|
||||
} else {
|
||||
tabOrWindowBrowser = tabOrWindow.gBrowser.selectedBrowser;
|
||||
}
|
||||
|
||||
let promiseRecordBounces = waitForRecordBounces(tabOrWindowBrowser);
|
||||
|
||||
// Navigate again with user gesture which triggers
|
||||
// BounceTrackingProtection::RecordStatefulBounces. We could rely on the
|
||||
// timeout (mClientBounceDetectionTimeout) here but that can cause races
|
||||
// in debug where the load is quite slow.
|
||||
await navigateLinkClick(
|
||||
tabOrWindowBrowser,
|
||||
new URL(getBaseUrl(ORIGIN_C) + "file_start.html")
|
||||
);
|
||||
|
||||
info("Wait for bounce trackers to be recorded.");
|
||||
await promiseRecordBounces;
|
||||
|
||||
// Cleanup popup or tab.
|
||||
if (spawnWindowType == "newTab") {
|
||||
await BrowserTestUtils.removeTab(tabOrWindow);
|
||||
} else {
|
||||
await BrowserTestUtils.closeWindow(tabOrWindow);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Check that the bounce tracker was detected.
|
||||
Assert.deepEqual(
|
||||
bounceTrackingProtection.testGetBounceTrackerCandidateHosts({}),
|
||||
[SITE_TRACKER],
|
||||
"Bounce tracker in popup detected."
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
bounceTrackingProtection.clearAll();
|
||||
await SiteDataTestUtils.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that bounce trackers which use popups as the first hop in the bounce
|
||||
* chain can not bypass detection.
|
||||
*
|
||||
* A -> popup -> B -> C
|
||||
*
|
||||
* A opens a popup and loads B in it. B is the tracker that performs a
|
||||
* short-lived redirect and C is the final destination.
|
||||
*/
|
||||
|
||||
add_task(async function test_popup() {
|
||||
await runTest("popup");
|
||||
});
|
||||
|
||||
add_task(async function test_new_tab() {
|
||||
await runTest("newTab");
|
||||
});
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { SiteDataTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/SiteDataTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"bounceTrackingProtection",
|
||||
"@mozilla.org/bounce-tracking-protection;1",
|
||||
"nsIBounceTrackingProtection"
|
||||
);
|
||||
|
||||
const SITE_A = "example.com";
|
||||
const ORIGIN_A = `https://${SITE_A}`;
|
||||
|
||||
|
@ -25,13 +36,6 @@ const OBSERVER_MSG_RECORD_BOUNCES_FINISHED = "test-record-bounces-finished";
|
|||
|
||||
const ROOT_DIR = getRootDirectory(gTestPath);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"bounceTrackingProtection",
|
||||
"@mozilla.org/bounce-tracking-protection;1",
|
||||
"nsIBounceTrackingProtection"
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the base url for the current test directory using the given origin.
|
||||
* @param {string} origin - Origin to use in URL.
|
||||
|
@ -122,23 +126,56 @@ function getBounceURL({
|
|||
* click on it.
|
||||
* @param {MozBrowser} browser - Browser to insert the link in.
|
||||
* @param {URL} targetURL - Destination for navigation.
|
||||
* @param {Object} options - Additional options.
|
||||
* @param {string} [options.spawnWindow] - If set to "newTab" or "popup" the
|
||||
* link will be opened in a new tab or popup window respectively. If unset the
|
||||
* link is opened in the given browser.
|
||||
* @returns {Promise} Resolves once the click is done. Does not wait for
|
||||
* navigation or load.
|
||||
*/
|
||||
async function navigateLinkClick(browser, targetURL) {
|
||||
await SpecialPowers.spawn(browser, [targetURL.href], targetURL => {
|
||||
let link = content.document.createElement("a");
|
||||
async function navigateLinkClick(
|
||||
browser,
|
||||
targetURL,
|
||||
{ spawnWindow = null } = {}
|
||||
) {
|
||||
if (spawnWindow && !["newTab", "popup"].includes(spawnWindow)) {
|
||||
throw new Error(`Invalid option '${spawnWindow}' for spawnWindow`);
|
||||
}
|
||||
|
||||
link.href = targetURL;
|
||||
link.textContent = targetURL;
|
||||
// The link needs display: block, otherwise synthesizeMouseAtCenter doesn't
|
||||
// hit it.
|
||||
link.style.display = "block";
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[targetURL.href, spawnWindow],
|
||||
async (targetURL, spawnWindow) => {
|
||||
let link = content.document.createElement("a");
|
||||
|
||||
content.document.body.appendChild(link);
|
||||
});
|
||||
// For opening a popup we attach an event listener to trigger via click.
|
||||
if (spawnWindow) {
|
||||
link.href = "#";
|
||||
link.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
if (spawnWindow == "newTab") {
|
||||
// Open a new tab.
|
||||
content.window.open(targetURL, "bounce");
|
||||
} else {
|
||||
// Open a popup window.
|
||||
content.window.open(targetURL, "bounce", "height=200,width=200");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// For regular navigation add href and click.
|
||||
link.href = targetURL;
|
||||
}
|
||||
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("a[href]", {}, browser);
|
||||
link.textContent = targetURL;
|
||||
// The link needs display: block, otherwise synthesizeMouseAtCenter doesn't
|
||||
// hit it.
|
||||
link.style.display = "block";
|
||||
|
||||
content.document.body.appendChild(link);
|
||||
|
||||
await EventUtils.synthesizeMouse(link, 1, 1, {}, content);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,6 +222,9 @@ async function waitForRecordBounces(browser) {
|
|||
* normal browsing.
|
||||
* @param {function} [options.postBounceCallback] - Optional function to run
|
||||
* after the bounce has completed.
|
||||
* @param {boolean} [options.skipSiteDataCleanup=false] - Skip the cleanup of
|
||||
* site data after the test. When this is enabled the caller is responsible for
|
||||
* cleaning up site data.
|
||||
*/
|
||||
async function runTestBounce(options = {}) {
|
||||
let {
|
||||
|
@ -197,6 +237,7 @@ async function runTestBounce(options = {}) {
|
|||
expectPurge = true,
|
||||
originAttributes = {},
|
||||
postBounceCallback = () => {},
|
||||
skipSiteDataCleanup = false,
|
||||
} = options;
|
||||
info(`runTestBounce ${JSON.stringify(options)}`);
|
||||
|
||||
|
@ -316,4 +357,7 @@ async function runTestBounce(options = {}) {
|
|||
);
|
||||
}
|
||||
bounceTrackingProtection.clearAll();
|
||||
if (!skipSiteDataCleanup) {
|
||||
await SiteDataTestUtils.clear();
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче