Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/**
* This test compares canvas randomization on an iframe and a blob: iframe within that iframe, and
* ensures that the canvas randomization key is inherited correctly. (e.g. that the canvases have the same
* random value.) There's three pages at play here: the parent frame, the iframe, and the blob iframe
* within the iframe. We only compare the inner-most two, we don't measure the outer one.
*
* It runs all the tests twice - once for when the iframe is cross-domain from the parent, and once when it is
* same-domain. But in both cases the blob iframe is same-domain to its parent.
*
* Covers the following cases:
* - RFP/FPP is disabled entirely
* - RFP is enabled entirely, and only in PBM
* - FPP is enabled entirely, and only in PBM
* - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
*
*/
"use strict";
// =============================================================================================
/**
* Compares two Uint8Arrays and returns the number of bits that are different.
*
* @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
* @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
* @returns {number} - The number of bits that are different between the two
* arrays.
*/
function countDifferencesInUint8Arrays(arr1, arr2) {
let count = 0;
for (let i = 0; i < arr1.length; i++) {
let diff = arr1[i] ^ arr2[i];
while (diff > 0) {
count += diff & 1;
diff >>= 1;
}
}
return count;
}
// =============================================================================================
async function testCanvasRandomization(result, expectedResults, extraData) {
let testDesc = extraData.testDesc;
let parent = result.mine;
let child = result.theirs;
let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
UNMODIFIED_CANVAS_DATA,
parent
);
let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
UNMODIFIED_CANVAS_DATA,
child
);
Assert.greaterOrEqual(
differencesFromUnmodifiedParent,
expectedResults[0],
`Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
);
Assert.lessOrEqual(
differencesFromUnmodifiedParent,
expectedResults[1],
`Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
);
Assert.greaterOrEqual(
differencesFromUnmodifiedChild,
expectedResults[0],
`Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
);
Assert.lessOrEqual(
differencesFromUnmodifiedChild,
expectedResults[1],
`Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
);
Assert.greaterOrEqual(
differencesInRandom,
expectedResults[2],
`Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
);
Assert.lessOrEqual(
differencesInRandom,
expectedResults[3],
`Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
);
}
requestLongerTimeout(2);
let expectedResults = {};
var UNMODIFIED_CANVAS_DATA = undefined;
add_setup(async function () {
// Disable the fingerprinting randomization.
await SpecialPowers.pushPrefEnv({
set: [
["privacy.fingerprintingProtection", false],
["privacy.fingerprintingProtection.pbmode", false],
["privacy.resistFingerprinting", false],
],
});
let extractCanvasData = function () {
let offscreenCanvas = new OffscreenCanvas(100, 100);
const context = offscreenCanvas.getContext("2d");
// Draw a red rectangle
context.fillStyle = "#EE2222";
context.fillRect(0, 0, 100, 100);
context.fillStyle = "#2222EE";
context.fillRect(20, 20, 100, 100);
const imageData = context.getImageData(0, 0, 100, 100);
return imageData.data;
};
function runExtractCanvasData(tab) {
let code = extractCanvasData.toString();
return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
await content.eval(`var extractCanvasData = ${funccode}`);
let result = await content.eval(`extractCanvasData()`);
return result;
});
}
const emptyPage =
getRootDirectory(gTestPath).replace(
) + "empty.html";
// Open a tab for extracting the canvas data.
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
let data = await runExtractCanvasData(tab);
UNMODIFIED_CANVAS_DATA = data;
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
// deep copy and avoiding corrupting the original 'const' object
// The first value represents the minimum number of random pixels we should see
// The second, the maximum number of random pixels
// The third, the minimum number of differences between the canvases of the parent and child
// The fourth, the maximum number of differences between the canvases of the parent and child
const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
const fppRandomizedSameDomain = [1, 260, 0, 0];
const noRandom = [0, 0, 0, 0];
// Note that we are inheriting the randomization key ACROSS top-level domains that are cross-domain, because the iframe is a 3rd party domain
let uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html`;
expectedResults = structuredClone(noRandom);
add_task(
defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
expectedResults = structuredClone(rfpFullyRandomized);
add_task(
simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test a private window with RFP enabled in PBMode
expectedResults = structuredClone(rfpFullyRandomized);
add_task(
simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test a Private Window with FPP Enabled in PBM
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
expectedResults = structuredClone(noRandom);
add_task(
RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
null,
uri,
testCanvasRandomization,
expectedResults
)
);
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
RFPPBMFPP_NormalMode_ProtectionsTest.bind(
null,
uri,
testCanvasRandomization,
expectedResults
)
);
// And here the we are inheriting the randomization key into an iframe that is same-domain to the parent
uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html`;
expectedResults = structuredClone(noRandom);
add_task(
defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
expectedResults = structuredClone(rfpFullyRandomized);
add_task(
simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test a private window with RFP enabled in PBMode
expectedResults = structuredClone(rfpFullyRandomized);
add_task(
simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test a Private Window with FPP Enabled in PBM
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
);
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
expectedResults = structuredClone(noRandom);
add_task(
RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
null,
uri,
testCanvasRandomization,
expectedResults
)
);
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
expectedResults = structuredClone(fppRandomizedSameDomain);
add_task(
RFPPBMFPP_NormalMode_ProtectionsTest.bind(
null,
uri,
testCanvasRandomization,
expectedResults
)
);