Bug 967895 - Add tests for canvas fingerprinting resistance. r=johannh
authorChung-Sheng Fu <cfu@mozilla.com>
Wed, 23 Aug 2017 16:32:41 +0800
changeset 386119 11d5208791fed061fdeb0e0ecd031dbcf676bf12
parent 386118 1e1c64f987b708e95a62beb39313ff6c637287de
child 386120 e0f3918c6e30229d4c69d7288c943a3709a6a4f7
push id53266
push userryanvm@gmail.com
push dateFri, 13 Oct 2017 15:34:43 +0000
treeherderautoland@11d5208791fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs967895
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 967895 - Add tests for canvas fingerprinting resistance. r=johannh MozReview-Commit-ID: 9Nysl1xD4M0
browser/base/content/test/permissions/browser.ini
browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js
--- a/browser/base/content/test/permissions/browser.ini
+++ b/browser/base/content/test/permissions/browser.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 support-files=
   head.js
   permissions.html
 
+[browser_canvas_fingerprinting_resistance.js]
 [browser_permissions.js]
 [browser_temporary_permissions.js]
 support-files =
   temporary_permissions_subframe.html
   ../webrtc/get_user_media.html
 [browser_temporary_permissions_expiry.js]
 [browser_temporary_permissions_navigation.js]
 [browser_temporary_permissions_tabs.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js
@@ -0,0 +1,122 @@
+/**
+ * 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.
+ */
+"use strict";
+
+const kUrl = "https://example.com/";
+const kPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+    .getService(Ci.nsIScriptSecurityManager)
+    .createCodebasePrincipal(Services.io.newURI(kUrl), {});
+const kPermission = "canvas/extractData";
+
+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);
+
+    return canvas;
+  };
+
+  let placeholder = drawCanvas("white");
+  contentWindow.kPlaceholderData = placeholder.toDataURL();
+  let canvas = drawCanvas("cyan", "canvas-id-canvas");
+  isnot(canvas.toDataURL(), contentWindow.kPlaceholderData,
+      "privacy.resistFingerprinting = false, canvas data != placeholder data");
+}
+
+function enableResistFingerprinting() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["privacy.resistFingerprinting", true]
+    ]
+  });
+}
+
+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) {
+    isnot(canvasData, contentWindow.kPlaceholderData,
+        "privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata");
+  } else if (grantPermission === false) {
+    is(canvasData, contentWindow.kPlaceholderData,
+        "privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata");
+  } else {
+    is(canvasData, contentWindow.kPlaceholderData,
+        "privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata");
+  }
+}
+
+function triggerCommand(button) {
+  let notifications = PopupNotifications.panel.childNodes;
+  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 withNewTab(grantPermission, browser) {
+  await ContentTask.spawn(browser, null, initTab);
+  await enableResistFingerprinting();
+  let popupShown = promisePopupShown();
+  await ContentTask.spawn(browser, null, 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 ContentTask.spawn(browser, grantPermission, extractCanvasData);
+  await SpecialPowers.popPrefEnv();
+}
+
+async function doTest(grantPermission) {
+  Services.perms.removeFromPrincipal(kPrincipal, kPermission);
+  await BrowserTestUtils.withNewTab(kUrl, withNewTab.bind(null, grantPermission));
+}
+
+// Tests clicking "Don't Allow" button of the permission prompt.
+add_task(doTest.bind(null, false));
+
+// Tests clicking "Allow" button of the permission prompt.
+add_task(doTest.bind(null, true));