зеркало из 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_oa_isolation.js"]
|
||||||
|
|
||||||
|
["browser_bouncetracking_popup.js"]
|
||||||
|
|
||||||
["browser_bouncetracking_purge.js"]
|
["browser_bouncetracking_purge.js"]
|
||||||
|
|
||||||
["browser_bouncetracking_schemes.js"]
|
["browser_bouncetracking_schemes.js"]
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { SiteDataTestUtils } = ChromeUtils.importESModule(
|
|
||||||
"resource://testing-common/SiteDataTestUtils.sys.mjs"
|
|
||||||
);
|
|
||||||
|
|
||||||
const TEST_ORIGIN = "https://itisatracker.org";
|
const TEST_ORIGIN = "https://itisatracker.org";
|
||||||
const TEST_BASE_DOMAIN = "itisatracker.org";
|
const TEST_BASE_DOMAIN = "itisatracker.org";
|
||||||
|
|
||||||
|
@ -28,6 +24,7 @@ async function runPurgeTest(expectPurge) {
|
||||||
await runTestBounce({
|
await runTestBounce({
|
||||||
bounceType: "client",
|
bounceType: "client",
|
||||||
setState: "localStorage",
|
setState: "localStorage",
|
||||||
|
skipSiteDataCleanup: true,
|
||||||
postBounceCallback: () => {
|
postBounceCallback: () => {
|
||||||
info(
|
info(
|
||||||
"Test that after the bounce but before purging cookies and localStorage are present."
|
"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";
|
"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 SITE_A = "example.com";
|
||||||
const ORIGIN_A = `https://${SITE_A}`;
|
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);
|
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.
|
* Get the base url for the current test directory using the given origin.
|
||||||
* @param {string} origin - Origin to use in URL.
|
* @param {string} origin - Origin to use in URL.
|
||||||
|
@ -122,23 +126,56 @@ function getBounceURL({
|
||||||
* click on it.
|
* click on it.
|
||||||
* @param {MozBrowser} browser - Browser to insert the link in.
|
* @param {MozBrowser} browser - Browser to insert the link in.
|
||||||
* @param {URL} targetURL - Destination for navigation.
|
* @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
|
* @returns {Promise} Resolves once the click is done. Does not wait for
|
||||||
* navigation or load.
|
* navigation or load.
|
||||||
*/
|
*/
|
||||||
async function navigateLinkClick(browser, targetURL) {
|
async function navigateLinkClick(
|
||||||
await SpecialPowers.spawn(browser, [targetURL.href], targetURL => {
|
browser,
|
||||||
let link = content.document.createElement("a");
|
targetURL,
|
||||||
|
{ spawnWindow = null } = {}
|
||||||
|
) {
|
||||||
|
if (spawnWindow && !["newTab", "popup"].includes(spawnWindow)) {
|
||||||
|
throw new Error(`Invalid option '${spawnWindow}' for spawnWindow`);
|
||||||
|
}
|
||||||
|
|
||||||
link.href = targetURL;
|
await SpecialPowers.spawn(
|
||||||
link.textContent = targetURL;
|
browser,
|
||||||
// The link needs display: block, otherwise synthesizeMouseAtCenter doesn't
|
[targetURL.href, spawnWindow],
|
||||||
// hit it.
|
async (targetURL, spawnWindow) => {
|
||||||
link.style.display = "block";
|
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.
|
* normal browsing.
|
||||||
* @param {function} [options.postBounceCallback] - Optional function to run
|
* @param {function} [options.postBounceCallback] - Optional function to run
|
||||||
* after the bounce has completed.
|
* 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 = {}) {
|
async function runTestBounce(options = {}) {
|
||||||
let {
|
let {
|
||||||
|
@ -197,6 +237,7 @@ async function runTestBounce(options = {}) {
|
||||||
expectPurge = true,
|
expectPurge = true,
|
||||||
originAttributes = {},
|
originAttributes = {},
|
||||||
postBounceCallback = () => {},
|
postBounceCallback = () => {},
|
||||||
|
skipSiteDataCleanup = false,
|
||||||
} = options;
|
} = options;
|
||||||
info(`runTestBounce ${JSON.stringify(options)}`);
|
info(`runTestBounce ${JSON.stringify(options)}`);
|
||||||
|
|
||||||
|
@ -316,4 +357,7 @@ async function runTestBounce(options = {}) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
bounceTrackingProtection.clearAll();
|
bounceTrackingProtection.clearAll();
|
||||||
|
if (!skipSiteDataCleanup) {
|
||||||
|
await SiteDataTestUtils.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче