summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js')
-rw-r--r--browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js383
1 files changed, 383 insertions, 0 deletions
diff --git a/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js b/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js
new file mode 100644
index 0000000000..dbb2d1ea32
--- /dev/null
+++ b/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js
@@ -0,0 +1,383 @@
+/**
+ * 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.
+ * When "privacy.resistFingerprinting.randomDataOnCanvasExtract" is true,
+ * canvas data extraction results in random data, and when it is false, canvas
+ * data extraction results in all-white 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 placeholder = drawCanvas("white");
+ contentWindow.kPlaceholderData = placeholder.toDataURL();
+ let canvas = drawCanvas("cyan", "canvas-id-canvas");
+ contentWindow.kPlacedData = canvas.toDataURL();
+ is(
+ canvas.toDataURL(),
+ contentWindow.kPlacedData,
+ "privacy.resistFingerprinting = false, canvas data == placed data"
+ );
+ isnot(
+ canvas.toDataURL(),
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = false, canvas data != placeholder data"
+ );
+}
+
+function enableResistFingerprinting(
+ randomDataOnCanvasExtract,
+ autoDeclineNoInput
+) {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.resistFingerprinting", true],
+ [
+ "privacy.resistFingerprinting.randomDataOnCanvasExtract",
+ randomDataOnCanvasExtract,
+ ],
+ [
+ "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts",
+ autoDeclineNoInput,
+ ],
+ ],
+ });
+}
+
+function promisePopupShown() {
+ return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+}
+
+function promisePopupHidden() {
+ return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
+}
+
+function extractCanvasData(randomDataOnCanvasExtract, 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"
+ );
+ if (!randomDataOnCanvasExtract) {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata"
+ );
+ }
+ } else if (grantPermission === false) {
+ isnot(
+ canvasData,
+ contentWindow.kPlacedData,
+ "privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
+ );
+ if (!randomDataOnCanvasExtract) {
+ is(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata"
+ );
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, permission denied, canvas data != placeholderdata"
+ );
+ }
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlacedData,
+ "privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
+ );
+ if (!randomDataOnCanvasExtract) {
+ is(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata"
+ );
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, requesting permission, canvas data != placeholderdata"
+ );
+ }
+ }
+}
+
+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(
+ randomDataOnCanvasExtract,
+ grantPermission,
+ browser
+) {
+ await SpecialPowers.spawn(browser, [], initTab);
+ await enableResistFingerprinting(randomDataOnCanvasExtract, false);
+ let popupShown = promisePopupShown();
+ await SpecialPowers.spawn(
+ browser,
+ [randomDataOnCanvasExtract],
+ 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,
+ [randomDataOnCanvasExtract, grantPermission],
+ extractCanvasData
+ );
+ await SpecialPowers.popPrefEnv();
+}
+
+async function doTestNoInput(randomDataOnCanvasExtract, grantPermission) {
+ await BrowserTestUtils.withNewTab(
+ kUrl,
+ withNewTabNoInput.bind(null, randomDataOnCanvasExtract, 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, true, false));
+add_task(doTestNoInput.bind(null, false, false));
+
+// Tests clicking "Allow" button of the permission prompt.
+add_task(doTestNoInput.bind(null, true, true));
+add_task(doTestNoInput.bind(null, false, true));
+
+async function withNewTabAutoBlockNoInput(randomDataOnCanvasExtract, browser) {
+ await SpecialPowers.spawn(browser, [], initTab);
+ await enableResistFingerprinting(randomDataOnCanvasExtract, 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,
+ [randomDataOnCanvasExtract],
+ 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(randomDataOnCanvasExtract) {
+ await BrowserTestUtils.withNewTab(
+ kUrl,
+ withNewTabAutoBlockNoInput.bind(null, randomDataOnCanvasExtract)
+ );
+}
+
+add_task(doTestAutoBlockNoInput.bind(null, true));
+add_task(doTestAutoBlockNoInput.bind(null, false));
+
+function extractCanvasDataUserInput(
+ randomDataOnCanvasExtract,
+ 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"
+ );
+ if (!randomDataOnCanvasExtract) {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata"
+ );
+ }
+ } else if (grantPermission === false) {
+ isnot(
+ canvasData,
+ contentWindow.kPlacedData,
+ "privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
+ );
+ if (!randomDataOnCanvasExtract) {
+ is(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata"
+ );
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, permission denied, canvas data != placeholderdata"
+ );
+ }
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlacedData,
+ "privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
+ );
+ if (!randomDataOnCanvasExtract) {
+ is(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata"
+ );
+ } else {
+ isnot(
+ canvasData,
+ contentWindow.kPlaceholderData,
+ "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, requesting permission, canvas data != placeholderdata"
+ );
+ }
+ }
+}
+
+async function withNewTabInput(
+ randomDataOnCanvasExtract,
+ grantPermission,
+ browser
+) {
+ await SpecialPowers.spawn(browser, [], initTab);
+ await enableResistFingerprinting(randomDataOnCanvasExtract, true);
+ let popupShown = promisePopupShown();
+ await SpecialPowers.spawn(browser, [], function (host) {
+ 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,
+ [randomDataOnCanvasExtract, grantPermission],
+ extractCanvasDataUserInput
+ );
+ await SpecialPowers.popPrefEnv();
+}
+
+async function doTestInput(
+ randomDataOnCanvasExtract,
+ grantPermission,
+ autoDeclineNoInput
+) {
+ await BrowserTestUtils.withNewTab(
+ kUrl,
+ withNewTabInput.bind(null, randomDataOnCanvasExtract, 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, true, false));
+add_task(doTestInput.bind(null, false, false));
+
+// Tests clicking "Allow" button of the permission prompt.
+add_task(doTestInput.bind(null, true, true));
+add_task(doTestInput.bind(null, false, true));