Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/**
* When "privacy.resistFingerprinting" is set to true, user permission is
* required for canvas data extraction.
* This tests whether the site permission prompt for canvas data extraction
* works properly.
* Canvas data extraction should result in random data.
*/
"use strict";
const kUrl = "https://example.com/";
const kPrincipal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(kUrl),
{}
);
const kPermission = "canvas";
function initTab() {
let contentWindow = content.wrappedJSObject;
let drawCanvas = (fillStyle, id) => {
let contentDocument = contentWindow.document;
let width = 64,
height = 64;
let canvas = contentDocument.createElement("canvas");
if (id) {
canvas.setAttribute("id", id);
}
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
contentDocument.body.appendChild(canvas);
let context = canvas.getContext("2d");
context.fillStyle = fillStyle;
context.fillRect(0, 0, width, height);
if (id) {
let button = contentDocument.createElement("button");
button.addEventListener("click", function () {
canvas.toDataURL();
});
button.setAttribute("id", "clickme");
button.innerHTML = "Click Me!";
contentDocument.body.appendChild(button);
}
return canvas;
};
let canvas = drawCanvas("cyan", "canvas-id-canvas");
contentWindow.kPlacedData = canvas.toDataURL();
is(
canvas.toDataURL(),
contentWindow.kPlacedData,
"privacy.resistFingerprinting = false, canvas data == placed data"
);
}
function enableResistFingerprinting(autoDeclineNoInput) {
return SpecialPowers.pushPrefEnv({
set: [
["privacy.fingerprintingProtection", true],
[
"privacy.fingerprintingProtection.overrides",
"+CanvasRandomization,+CanvasImageExtractionPrompt,+CanvasExtractionFromThirdPartiesIsBlocked" +
(autoDeclineNoInput
? ",+CanvasExtractionBeforeUserInputIsBlocked"
: ""),
],
],
});
}
function promisePopupShown() {
return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
}
function promisePopupHidden() {
return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
}
function extractCanvasData(grantPermission) {
let contentWindow = content.wrappedJSObject;
let canvas = contentWindow.document.getElementById("canvas-id-canvas");
let canvasData = canvas.toDataURL();
if (grantPermission) {
is(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, permission granted, canvas data == placed data"
);
} else if (grantPermission === false) {
isnot(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
);
} else {
isnot(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
);
}
}
function triggerCommand(button) {
let notifications = PopupNotifications.panel.children;
let notification = notifications[0];
EventUtils.synthesizeMouseAtCenter(notification[button], {});
}
function triggerMainCommand() {
triggerCommand("button");
}
function triggerSecondaryCommand() {
triggerCommand("secondaryButton");
}
function testPermission() {
return Services.perms.testPermissionFromPrincipal(kPrincipal, kPermission);
}
async function withNewTabNoInput(grantPermission, browser) {
await SpecialPowers.spawn(browser, [], initTab);
await enableResistFingerprinting(false);
let popupShown = promisePopupShown();
await SpecialPowers.spawn(browser, [], extractCanvasData);
await popupShown;
let popupHidden = promisePopupHidden();
if (grantPermission) {
triggerMainCommand();
await popupHidden;
is(testPermission(), Services.perms.ALLOW_ACTION, "permission granted");
} else {
triggerSecondaryCommand();
await popupHidden;
is(testPermission(), Services.perms.DENY_ACTION, "permission denied");
}
await SpecialPowers.spawn(browser, [grantPermission], extractCanvasData);
await SpecialPowers.popPrefEnv();
}
async function doTestNoInput(grantPermission) {
await BrowserTestUtils.withNewTab(
kUrl,
withNewTabNoInput.bind(null, grantPermission)
);
Services.perms.removeFromPrincipal(kPrincipal, kPermission);
}
// With auto-declining disabled (not the default)
// Tests clicking "Don't Allow" button of the permission prompt.
add_task(doTestNoInput.bind(null, false));
// Tests clicking "Allow" button of the permission prompt.
add_task(doTestNoInput.bind(null, true));
async function withNewTabAutoBlockNoInput(browser) {
await SpecialPowers.spawn(browser, [], initTab);
await enableResistFingerprinting(true);
let noShowHandler = () => {
ok(false, "The popup notification should not show in this case.");
};
PopupNotifications.panel.addEventListener("popupshown", noShowHandler, {
once: true,
});
let promisePopupObserver = TestUtils.topicObserved(
"PopupNotifications-updateNotShowing"
);
// Try to extract canvas data without user inputs.
await SpecialPowers.spawn(browser, [], extractCanvasData);
await promisePopupObserver;
info("There should be no popup shown on the panel.");
// Check that the icon of canvas permission is shown.
let canvasNotification = PopupNotifications.getNotification(
"canvas-permissions-prompt",
browser
);
is(
canvasNotification.anchorElement.getAttribute("showing"),
"true",
"The canvas permission icon is correctly shown."
);
PopupNotifications.panel.removeEventListener("popupshown", noShowHandler);
await SpecialPowers.popPrefEnv();
}
async function doTestAutoBlockNoInput() {
await BrowserTestUtils.withNewTab(
kUrl,
withNewTabAutoBlockNoInput.bind(null)
);
}
add_task(doTestAutoBlockNoInput.bind(null));
function extractCanvasDataUserInput(grantPermission) {
let contentWindow = content.wrappedJSObject;
let canvas = contentWindow.document.getElementById("canvas-id-canvas");
let canvasData = canvas.toDataURL();
if (grantPermission) {
is(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, permission granted, canvas data == placed data"
);
} else if (grantPermission === false) {
isnot(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
);
} else {
isnot(
canvasData,
contentWindow.kPlacedData,
"privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
);
}
}
async function withNewTabInput(grantPermission, browser) {
await SpecialPowers.spawn(browser, [], initTab);
await enableResistFingerprinting(true);
let popupShown = promisePopupShown();
await SpecialPowers.spawn(browser, [], function () {
E10SUtils.wrapHandlingUserInput(content, true, function () {
var button = content.document.getElementById("clickme");
button.click();
});
});
await popupShown;
let popupHidden = promisePopupHidden();
if (grantPermission) {
triggerMainCommand();
await popupHidden;
is(testPermission(), Services.perms.ALLOW_ACTION, "permission granted");
} else {
triggerSecondaryCommand();
await popupHidden;
is(testPermission(), Services.perms.DENY_ACTION, "permission denied");
}
await SpecialPowers.spawn(
browser,
[grantPermission],
extractCanvasDataUserInput
);
await SpecialPowers.popPrefEnv();
}
async function doTestInput(grantPermission) {
await BrowserTestUtils.withNewTab(
kUrl,
withNewTabInput.bind(null, grantPermission)
);
Services.perms.removeFromPrincipal(kPrincipal, kPermission);
}
// With auto-declining enabled (the default)
// Tests clicking "Don't Allow" button of the permission prompt.
add_task(doTestInput.bind(null, false));
// Tests clicking "Allow" button of the permission prompt.
add_task(doTestInput.bind(null, true));