Commit 73c7fe8d authored by Fatih's avatar Fatih
Browse files

Bug 1918690: Improve canvas randomization permission test. r=tjr

parent c90c9255
Loading
Loading
Loading
Loading
+100 −28
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@

/**
 * Bug 1896175 - Testing canvas randomization with read canvas permission granted.
 *
 * Bug 1918690 - Extend canvas randomization permission test
 */

const emptyPage =
@@ -13,11 +13,11 @@ const emptyPage =
    "https://example.com"
  ) + "empty.html";

const TRUE_VALUE = "255,0,0,255,0,255,0,255,0,0,255,255";

function getImageData(tab) {
  const extractCanvas = function () {
    const canvas = document.createElement("canvas");
function getImageData(tab, isOffscreen, method) {
  const extractCanvas = function (isOffscreen, method) {
    const canvas = isOffscreen
      ? new OffscreenCanvas(3, 1)
      : document.createElement("canvas");
    const ctx = canvas.getContext("2d");

    ctx.fillStyle = "rgb(255, 0, 0)";
@@ -27,10 +27,30 @@ function getImageData(tab) {
    ctx.fillStyle = "rgb(0, 0, 255)";
    ctx.fillRect(2, 0, 1, 1);

    if (method === "getImageData") {
      return ctx.getImageData(0, 0, 3, 1).data.join(",");
    } else if (method === "toDataURL") {
      return canvas.toDataURL();
    } else if (method === "toBlob") {
      return new Promise((resolve, reject) => {
        if (isOffscreen) {
          canvas.convertToBlob().then(resolve, reject);
        } else {
          canvas.toBlob(resolve, "image/png");
        }
      }).then(blob => {
        const reader = new FileReader();
        reader.readAsDataURL(blob);
        const { promise, resolve, reject } = Promise.withResolvers();
        reader.onload = () => resolve(reader.result);
        reader.onerror = reject;
        return promise;
      });
    }
    return null;
  };

  const extractCanvasExpr = `(${extractCanvas.toString()})();`;
  const extractCanvasExpr = `(${extractCanvas.toString()})(${isOffscreen}, "${method}");`;

  return SpecialPowers.spawn(
    tab.linkedBrowser,
@@ -39,10 +59,21 @@ function getImageData(tab) {
  );
}

add_task(async function test_no_permission() {
// Test cases: [isOffscreen, method, true value]
const TEST_CASES = [
  // HTMLCanvasElement
  [false, "getImageData", null],
  [false, "toDataURL", null],
  [false, "toBlob", null],
  // OffscreenCanvas
  [true, "getImageData", null],
  [true, "toBlob", null],
];

add_setup(async function setup() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.resistFingerprinting", true],
      ["privacy.resistFingerprinting", false],
      ["privacy.fingerprintingProtection", false],
    ],
  });
@@ -53,19 +84,26 @@ add_task(async function test_no_permission() {
    forceNewProcess: true,
  });

  const imageData = await getImageData(tab);

  isnot(imageData, TRUE_VALUE, "Image data was randomized.");
  for (let i = 0; i < TEST_CASES.length; i++) {
    const test_case = TEST_CASES[i];
    const [isOffscreen, method] = test_case;
    const imageData = await getImageData(tab, isOffscreen, method);
    test_case[2] = imageData;
  }

  BrowserTestUtils.removeTab(tab);
  await SpecialPowers.popPrefEnv();
});

add_task(async function test_permission() {
async function runTest(permissionGranted, isRFP) {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.resistFingerprinting", true],
      ["privacy.fingerprintingProtection", false],
      ["privacy.resistFingerprinting", isRFP],
      ["privacy.fingerprintingProtection", !isRFP],
      [
        "privacy.fingerprintingProtection.overrides",
        "-AllTargets,+CanvasRandomization,+CanvasImageExtractionPrompt",
      ],
    ],
  });

@@ -75,6 +113,7 @@ add_task(async function test_permission() {
    forceNewProcess: true,
  });

  if (permissionGranted) {
    await SpecialPowers.pushPermissions([
      {
        type: "canvas",
@@ -82,12 +121,45 @@ add_task(async function test_permission() {
        context: emptyPage,
      },
    ]);
  }

  const imageData = await getImageData(tab);

  is(imageData, TRUE_VALUE, "Image data was not randomized.");
  for (let i = 0; i < TEST_CASES.length; i++) {
    const test_case = TEST_CASES[i];
    const [isOffscreen, method, trueValue] = test_case;
    const imageData = await getImageData(tab, isOffscreen, method);
    const message = `Image data was ${
      permissionGranted ? "not " : ""
    }randomized for ${method} on ${
      isOffscreen ? "OffscreenCanvas" : "HTMLCanvasElement"
    }.`;
    if (isOffscreen) {
      // Offscreen canvas doesn't respect the permission.
      continue;
    }
    permissionGranted
      ? is(imageData, trueValue, message)
      : isnot(imageData, trueValue, message);
  }

  BrowserTestUtils.removeTab(tab);
  if (permissionGranted) {
    await SpecialPowers.popPermissions();
  }
  BrowserTestUtils.removeTab(tab);
  await SpecialPowers.popPrefEnv();
}

add_task(async function test_rfp_no_permission() {
  await runTest(false, true);
});

add_task(async function test_rfp_permission() {
  await runTest(true, true);
});

add_task(async function test_fpp_no_permission() {
  await runTest(false, false);
});

add_task(async function test_fpp_permission() {
  await runTest(true, false);
});