Bug 1217238 - Regression tests for reducing precision of time exposed by Javascript. r=mystor
authorJonathan Hao <jhao@mozilla.com>
Tue, 06 Jun 2017 11:45:13 +0800
changeset 411293 e967714a341a26b64ce118edb3172faa35fbd4b3
parent 411292 7bc19dddce5d264b0cc4029015caae6277fce01c
child 411294 1a7356fac9ba0d1f5cb8651db78bb62dad50b3e5
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmystor
bugs1217238, 1517
milestone55.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 1217238 - Regression tests for reducing precision of time exposed by Javascript. r=mystor This patch is adapted from Tor bug 1517. Test that the following javascript codes will return timeStamps that are rounded to 100ms. performance.now() new Date().getTime() new Event("").timeStamp new File([], "").lastModified new File([], "").lastModifiedDate.getTime() audioContext.currentTime * 1000 canvasStream.currentTime * 1000 video.currentTime * 1000 audio.currentTime * 1000 The first five codes are also tested in simple workers and nested workers, created before and after the pref is on, to ensure that the pref has correctly propagated. MozReview-Commit-ID: CuoxmGRrBnm
browser/components/resistfingerprinting/moz.build
browser/components/resistfingerprinting/test/mochitest/.eslintrc.js
browser/components/resistfingerprinting/test/mochitest/mochitest.ini
browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
browser/components/resistfingerprinting/test/mochitest/worker_child.js
browser/components/resistfingerprinting/test/mochitest/worker_grandchild.js
--- a/browser/components/resistfingerprinting/moz.build
+++ b/browser/components/resistfingerprinting/moz.build
@@ -5,8 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Security")
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
 ]
+
+MOCHITEST_MANIFESTS += [
+    'test/mochitest/mochitest.ini',
+]
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/.eslintrc.js
@@ -0,0 +1,5 @@
+module.exports = {
+  "rules": {
+    "no-eval": "off"
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/mochitest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+  worker_child.js
+  worker_grandchild.js
+
+[test_reduce_time_precision.html]
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tor bug
+https://trac.torproject.org/projects/tor/ticket/1517
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Tor Bug 1517</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://trac.torproject.org/projects/tor/ticket/1517">Tor Bug 1517</a>
+
+<!-- Canvas for testing 'currentTime' -->
+<canvas id="test-canvas" width="100" height="100"></canvas>
+
+<!-- The main testing script -->
+<script type="application/javascript;version=1.7">
+  SimpleTest.requestFlakyTimeout("testing JS time-based fingerprinting");
+
+  // Prepare for test of AudioContext.currentTime
+  let audioContext = new AudioContext();
+  // Prepare for test of CanvasStream.currentTime
+  let canvas = document.getElementById("test-canvas");
+  let context = canvas.getContext('2d');
+  context.fillText("test", 20, 20);
+  let canvasStream = canvas.captureStream(25);
+
+  // Known ways to generate time stamps, in milliseconds
+  const timeStampCodes = [
+    'performance.now()',
+    'new Date().getTime()',
+    'new Event("").timeStamp',
+    'new File([], "").lastModified',
+    'new File([], "").lastModifiedDate.getTime()',
+  ];
+
+  const kExpectedResolution = 100;
+
+  function* checkWorker(worker) {
+    // The child worker will send the results back.
+    let checkTimeStamps = () => new Promise(function(resolve) {
+      let onMessage = function(event) {
+        worker.removeEventListener("message", onMessage);
+
+        let timeStamps = event.data;
+        for (let i = 0; i < timeStampCodes.length; i++) {
+          let timeStamp = timeStamps[i];
+          is(timeStamp % kExpectedResolution, 0,
+             "'" + timeStampCodes[i] +
+             "' should be rounded to nearest 100 ms in workers; saw " +
+             timeStamp);
+        }
+        resolve();
+      };
+      worker.addEventListener("message", onMessage);
+    });
+
+    // Send the codes to its child worker.
+    worker.postMessage(timeStampCodes);
+
+    // First, check the child's results.
+    yield checkTimeStamps();
+    // Then, check the grandchild's results.
+    yield checkTimeStamps();
+
+    worker.terminate();
+  }
+
+  add_task(function* testWorker() {
+    // Create one worker before setting the pref, and one after, in order to
+    // check that the resolution is updated whether or not the worker was
+    // already started
+    let worker1 = new Worker("worker_child.js");
+    yield SpecialPowers.pushPrefEnv({
+      "set": [["privacy.resistFingerprinting", true]]});
+    let worker2 = new Worker("worker_child.js");
+    // Allow ~550 ms to elapse, so we can get non-zero
+    // time values for all elements.
+    yield new Promise(resolve => window.setTimeout(resolve, 550));
+    yield checkWorker(worker1);
+    yield checkWorker(worker2);
+  });
+
+  add_task(function* testDOM() {
+    let timeStampCodesDOM = timeStampCodes.concat([
+      'audioContext.currentTime * 1000',
+      'canvasStream.currentTime * 1000',
+    ]);
+    // Loop through each timeStampCode, evaluate it,
+    // and check if it is rounded to the nearest 100 ms.
+    for (let timeStampCode of timeStampCodesDOM) {
+      let timeStamp = eval(timeStampCode);
+      is(timeStamp % kExpectedResolution, 0,
+         "'" + timeStampCode +
+         "' should be rounded to nearest 100 ms; saw " +
+         timeStamp);
+    }
+  });
+
+</script>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/worker_child.js
@@ -0,0 +1,28 @@
+let timeStampCodes;
+let worker = new Worker("worker_grandchild.js");
+
+function listenToParent(event) {
+  self.removeEventListener("message", listenToParent);
+  timeStampCodes = event.data;
+
+  let timeStamps = [];
+  for (let timeStampCode of timeStampCodes) {
+    timeStamps.push(eval(timeStampCode));
+  }
+  // Send the timeStamps to the parent.
+  postMessage(timeStamps);
+
+  // Tell the grandchild to start.
+  worker.postMessage(timeStampCodes);
+}
+
+// The worker grandchild will send results back.
+function listenToChild(event) {
+  worker.removeEventListener("message", listenToChild);
+  // Pass the results to the parent.
+  postMessage(event.data);
+  worker.terminate();
+}
+
+worker.addEventListener("message", listenToChild);
+self.addEventListener("message", listenToParent);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/worker_grandchild.js
@@ -0,0 +1,10 @@
+self.addEventListener("message", function(event) {
+  let timeStampCodes = event.data;
+
+  let timeStamps = [];
+  for (let timeStampCode of timeStampCodes) {
+    timeStamps.push(eval(timeStampCode));
+  }
+  // Send the timeStamps to the parent.
+  postMessage(timeStamps);
+});