diff options
Diffstat (limited to 'browser/components/resistfingerprinting/test/mochitest')
18 files changed, 1012 insertions, 0 deletions
diff --git a/browser/components/resistfingerprinting/test/mochitest/.eslintrc.js b/browser/components/resistfingerprinting/test/mochitest/.eslintrc.js new file mode 100644 index 0000000000..16ee78885f --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + rules: { + "no-eval": "off", + }, +}; diff --git a/browser/components/resistfingerprinting/test/mochitest/decode_error.mp4 b/browser/components/resistfingerprinting/test/mochitest/decode_error.mp4 Binary files differnew file mode 100644 index 0000000000..ee72e2dd75 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/decode_error.mp4 diff --git a/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html b/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html new file mode 100644 index 0000000000..376fddf343 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html @@ -0,0 +1,104 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1382545</title> +<script> + function waitForCondition(aCond, aCallback, aErrorMsg) { + var tries = 0; + var interval = setInterval(() => { + if (tries >= 30) { + opener.ok(false, aErrorMsg); + moveOn(); + return; + } + var conditionPassed; + try { + conditionPassed = aCond(); + } catch (e) { + opener.ok(false, `${e}\n${e.stack}`); + conditionPassed = false; + } + if (conditionPassed) { + moveOn(); + } + tries++; + }, 100); + var moveOn = () => { clearInterval(interval); aCallback(); }; + } + + function runTest() { + let expectedPrecision = opener.expectedPrecision / 1000; + let isRounded = (x) => { + let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision); + // First we do the perfectly normal check that should work just fine + if (rounded === x || x === 0) + return true; + + // When we're diving by non-whole numbers, we may not get perfect + // multiplication/division because of floating points. + // When dealing with ms since epoch, a double's precision is on the order + // of 1/5 of a microsecond, so we use a value a little higher than that as + // our epsilon. + // To be clear, this error is introduced in our re-calculation of 'rounded' + // above in JavaScript. + if (Math.abs(rounded - x + expectedPrecision) < .0005) { + return true; + } else if (Math.abs(rounded - x) < .0005) { + return true; + } + + // Then we handle the case where you're sub-millisecond and the timer is not + // We check that the timer is not sub-millisecond by assuming it is not if it + // returns an even number of milliseconds + if (expectedPrecision < 1 && Math.round(x) == x) { + if (Math.round(rounded) == x) { + return true; + } + } + + // We are temporarily disabling this extra debugging failure because we expect to return false in some instances + // When we correct things we will re-enable it for debugging assistance + // opener.ok(false, "Looming Test Failure, Additional Debugging Info: Expected Precision: " + expectedPrecision + " Measured Value: " + x + + // " Rounded Vaue: " + rounded + " Fuzzy1: " + Math.abs(rounded - x + expectedPrecision) + + // " Fuzzy 2: " + Math.abs(rounded - x)); + + return false; + }; + const testDiv = document.getElementById("testDiv"); + const animation = testDiv.animate({ opacity: [0, 1] }, 100000); + animation.play(); + + waitForCondition( + () => animation.currentTime > 100, + () => { + // We have disabled Time Precision Reduction for CSS Animations, so we expect those tests to fail. + // If we are testing that preference, we accept either rounded or not rounded values as A-OK. + var maybeAcceptEverything = function(value) { + if (opener.prefName.includes("privacy.reduceTimerPrecision") && + !opener.prefName.includes("privacy.resistFingerprinting")) + return true; + return value; + }; + + opener.ok(maybeAcceptEverything(isRounded(animation.startTime)), + "pref: " + opener.prefName + " - animation.startTime with precision " + expectedPrecision + " is not rounded: " + animation.startTime); + opener.ok(maybeAcceptEverything(isRounded(animation.currentTime)), + "pref: " + opener.prefName + " - animation.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.currentTime); + opener.ok(maybeAcceptEverything(isRounded(animation.timeline.currentTime)), + "pref: " + opener.prefName + " - animation.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.timeline.currentTime); + if (document.timeline) { + opener.ok(maybeAcceptEverything(isRounded(document.timeline.currentTime)), + "pref: " + opener.prefName + " - document.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + document.timeline.currentTime); + } + opener.done(); + window.close(); + }, + "animation failed to start"); + } +</script> +</head> +<body onload="runTest();"> +<div id="testDiv">test</div> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/mochitest.ini b/browser/components/resistfingerprinting/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..2ebbc0382f --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/mochitest.ini @@ -0,0 +1,29 @@ +[DEFAULT] +skip-if = toolkit == 'android' # bug 1730213 +tags = resistfingerprinting + +support-files = + file_animation_api.html + worker_child.js + worker_grandchild.js + !/dom/tests/mochitest/geolocation/network_geolocation.sjs + +[test_animation_api.html] +[test_device_sensor_event.html] +[test_geolocation.html] +scheme = https +fail-if = xorigin +[test_hide_gamepad_info.html] +scheme = https +support-files = test_hide_gamepad_info_iframe.html +[test_iframe.html] +[test_keyboard_event.html] +[test_pointer_event.html] + support-files = + ../../../../../dom/events/test/pointerevents/mochitest_support_external.js +[test_speech_synthesis.html] +skip-if = verify +[test_bug1382499_touch_api.html] +[test_bug863246_resource_uri.html] +[test_bug1354633_media_error.html] +support-files = decode_error.mp4 diff --git a/browser/components/resistfingerprinting/test/mochitest/test_animation_api.html b/browser/components/resistfingerprinting/test/mochitest/test_animation_api.html new file mode 100644 index 0000000000..74f6f3083e --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_animation_api.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1382545 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1382545</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1382545 **/ + SimpleTest.waitForExplicitFinish(); + + // Used by file_animation_api.html + var prefName = ""; + var expectedPrecision = 0; + var resistFingerprinting = false; + var reduceTimerPrecision = false; + + function runTest() { + // No matter what we set the precision to, if we're in ResistFingerprinting mode + // we use the larger of the precision pref and the constant RFP time-atom + if (resistFingerprinting) { + const RFP_TIME_ATOM_MS = 16.667; + expectedPrecision = Math.max(1000*RFP_TIME_ATOM_MS, expectedPrecision); + } + window.open("file_animation_api.html"); + } + + function setupTest(rfp, rtp, ep) { + // Set globals + expectedPrecision = ep; + resistFingerprinting = rfp; + reduceTimerPrecision = rtp; + prefName = ""; + prefName += resistFingerprinting ? "privacy.resistFingerprinting " : ""; + prefName += reduceTimerPrecision ? "privacy.reduceTimerPrecision " : ""; + SpecialPowers.pushPrefEnv({"set": + [ + ["dom.animations-api.timelines.enabled", true], + ["privacy.resistFingerprinting", resistFingerprinting], + ["privacy.reduceTimerPrecision", reduceTimerPrecision], + ["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision], + ], + }, runTest); + } + + var testIndx = 0; + var testSequence = [ + [true, false, 100000], + [false, true, 100000], + [true, false, 50000], + [false, true, 50000], + [true, false, 100], + [false, true, 100], + [true, true, 13], + [false, true, 13], + ]; + + window.onload = () => { + setupTest(testSequence[testIndx][0], testSequence[testIndx][1], testSequence[testIndx][2]); + }; + + function done() { + testIndx++; + if (testIndx == testSequence.length) { + SimpleTest.finish(); + } else { + setupTest(testSequence[testIndx][0], testSequence[testIndx][1], testSequence[testIndx][2]); + } + } + </script> +</head> +<body> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_bug1354633_media_error.html b/browser/components/resistfingerprinting/test/mochitest/test_bug1354633_media_error.html new file mode 100644 index 0000000000..a53a3ad34a --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_bug1354633_media_error.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers */ + +let errorMessageMap = {}; + +let testPromise = (resistFingerprinting, src, whitelist) => new Promise(resolve => { + let video = document.createElement("video"); + video.src = src; + video.controls = "true"; + video.onerror = () => { + let message = video.error.message; + if (!resistFingerprinting) { + SimpleTest.isnot(message, "", "Message should not be blank"); + SimpleTest.info(src + ": " + message); + errorMessageMap[src] = message; + } else if (whitelist) { + SimpleTest.is(message, whitelist, "Error message in whitelist: " + whitelist); + } else { + SimpleTest.is(message, "", "Blank error message: " + errorMessageMap[src]); + } + resolve(); + }; + document.body.appendChild(video); +}); + +async function testBody(resistFingerprinting) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.resistFingerprinting", resistFingerprinting], + ], + }); + await testPromise( + resistFingerprinting, + "load_error.mp4", + "404: Not Found" // whitelist + ); + await testPromise( + resistFingerprinting, + "decode_error.mp4", + false // whitelist + ); +} + +SimpleTest.waitForExplicitFinish(); +document.addEventListener("DOMContentLoaded", async () => { + await testBody(false); + await testBody(true); + SimpleTest.finish(); +}); +</script> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_bug1382499_touch_api.html b/browser/components/resistfingerprinting/test/mochitest/test_bug1382499_touch_api.html new file mode 100644 index 0000000000..45743f3dc3 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_bug1382499_touch_api.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script> +/* global SimpleTest SpecialPowers synthesizeTouch */ + +SimpleTest.waitForExplicitFinish(); + +function promiseEvent(target, eventName) { + return new Promise(resolve => { + target.addEventListener(eventName, resolve, {once: true}); + }); +} + +function promiseTouchEvent(target, type, offsetX, offsetY, params) { + let touchEventPromise = promiseEvent(target, type); + params.type = type; + synthesizeTouch(target, offsetX, offsetY, params); + return touchEventPromise; +} + +document.addEventListener("DOMContentLoaded", async () => { + const target0 = document.getElementById("target0"); + const touchParams = {force: 1.0, angle: 1.0, rx: 2, ry: 3}; + await SpecialPowers.pushPrefEnv({set: [["dom.w3c_touch_events.enabled", 1]]}); + for (let resist of [false, true]) { + await SpecialPowers.pushPrefEnv({set: [["privacy.resistFingerprinting", resist]]}); + info("starting test with fingerprinting resistance " + (resist ? "on" : "off")); + let touchEvent = await promiseTouchEvent(target0, "touchstart", 5, 5, touchParams); + info("touch event received"); + let touch = touchEvent.touches[0]; + if (resist) { + is(touch.screenX, touch.clientX, "touch.screenX should be the same as touch.clientX"); + is(touch.screenY, touch.clientY, "touch.screenY should be the same as touch.clientY"); + // radiusX/radiusY may differ from the original rx/ry because of AppUnitsPerCSSPixel and AppUnitsPerDevPixel. + // So only check if the values are spoofed. + is(touch.radiusX, 0, "touch.radiusX"); + is(touch.radiusY, 0, "touch.radiusY"); + } + is(touch.force, resist ? 0.0 : touchParams.force, "touch.force"); + is(touch.rotationAngle, resist ? 0 : touchParams.angle, "touch.rotationAngle"); + } + SimpleTest.finish(); +}); +</script> +<div id="target0">target 0</div> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_bug863246_resource_uri.html b/browser/components/resistfingerprinting/test/mochitest/test_bug863246_resource_uri.html new file mode 100644 index 0000000000..fda1c04200 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_bug863246_resource_uri.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf8"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers add_task */ + +function testResourceUri(aTest, aUri, aContentAccessible) { + return new Promise((aResolve) => { + let link = document.createElement("link"); + link.rel = "stylesheet"; + link.onload = () => { + SimpleTest.ok(aContentAccessible, aTest); + aResolve(); + }; + link.onerror = () => { + SimpleTest.ok(!aContentAccessible, aTest); + aResolve(); + }; + link.href = aUri; + document.head.appendChild(link); + }); +} + +add_task(async function() { + await testResourceUri( + "resource://content-accessible is content-accessible", + "resource://content-accessible/viewsource.css", + true); + await testResourceUri( + "resource://gre-resources is not content-accessible", + "resource://gre-resources/html.css", + false); + await SpecialPowers.pushPrefEnv({ + set: [ + ["security.all_resource_uri_content_accessible", true], + ], + }); + await testResourceUri( + "security.all_resource_uri_content_accessible = true, resource://gre-resources is now content-accessible", + "resource://gre-resources/html.css", + true); +}); +</script> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_device_sensor_event.html b/browser/components/resistfingerprinting/test/mochitest/test_device_sensor_event.html new file mode 100644 index 0000000000..afc6bbfb40 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_device_sensor_event.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1369319 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1369319</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1369319 **/ + SimpleTest.waitForExplicitFinish(); + window.onload = () => { + SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({"set": + [ + ["device.sensors.test.events", true], + ["privacy.resistFingerprinting", true], + ], + }, doTest); + }, window); + }; + + function doTest() { + window.addEventListener("devicemotion", () => { + ok(false, "The device motion event should not be fired."); + }, {once: true}); + + window.addEventListener("TestEvent", () => { + // If we receive this event without receiving a 'devicemotion' event, this means + // the device sensor event has been blocked correctly. + ok(true, "Got the 'TestEvent' event."); + SimpleTest.finish(); + }, {once: true}); + + window.dispatchEvent(new CustomEvent("TestEvent")); + } + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"></pre> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_geolocation.html b/browser/components/resistfingerprinting/test/mochitest/test_geolocation.html new file mode 100644 index 0000000000..7dc2059dd9 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_geolocation.html @@ -0,0 +1,68 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1372069 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1372069</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + const BASE_GEO_URL = "http://mochi.test:8888/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs"; + + /** Test for Bug 1372069 **/ + /** Modified for Bug 1441295 **/ + SimpleTest.waitForExplicitFinish(); + window.onload = () => { + SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({"set": + [ + ["privacy.resistFingerprinting", true], + ["geo.prompt.testing", true], + ["geo.prompt.testing.allow", true], + ["geo.provider.network.url", BASE_GEO_URL], + ], + }, doTest_getCurrentPosition); + }, window); + }; + + function doTest_getCurrentPosition() { + navigator.geolocation.getCurrentPosition( + (position) => { + ok(true, "Success callback is expected to be called"); + doTest_watchPosition(); + }, + (error) => { + ok(false, "Should be able to call success callback, Got error. code = " + error.code); + doTest_watchPosition(); + } + ); + } + + function doTest_watchPosition() { + let wid = navigator.geolocation.watchPosition( + (position) => { + ok(true, "Success callback is expected to be called"); + navigator.geolocation.clearWatch(wid); + SimpleTest.finish(); + }, + (error) => { + ok(false, "Should be able to call success callback, Got error. code = " + error.code); + navigator.geolocation.clearWatch(wid); + SimpleTest.finish(); + } + ); + } + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"></pre> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info.html b/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info.html new file mode 100644 index 0000000000..08fdb0c852 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf8"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers */ + +SimpleTest.waitForExplicitFinish(); +document.addEventListener("DOMContentLoaded", function() { + SpecialPowers.pushPrefEnv({ + set: [ + ["dom.gamepad.test.enabled", true], + ["privacy.resistFingerprinting", true], + ], + }, function() { + // This test loads in an iframe, to ensure that the navigator instance is + // loaded with the correct value of the preference. + var iframe = document.createElement("iframe"); + iframe.allow = "gamepad"; + iframe.src = "test_hide_gamepad_info_iframe.html"; + document.body.appendChild(iframe); + }); +}); +</script> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info_iframe.html b/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info_iframe.html new file mode 100644 index 0000000000..5946e1ce6e --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_hide_gamepad_info_iframe.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<meta charset="utf8"> +<!--<script src="/tests/SimpleTest/SimpleTest.js"></script>--> +<script> +var SimpleTest = window.parent.SimpleTest; + +function forceFail() { + SimpleTest.ok( + false, + "privacy.resistFingerprinting is true, should not receive any gamepad events" + ); +} + +window.addEventListener("gamepadconnected", forceFail); +window.addEventListener("gamepaddisconnected", forceFail); +window.addEventListener("gamepadbuttondown", forceFail); + +window.addEventListener("load", async () => { + const service = navigator.requestGamepadServiceTest(); + const buttonIndex = await service.addGamepad( + "test gamepad", // id + service.standardMapping, + service.noHand, + 4, // buttons + 2, + 0, + 0, + 0 + ); + + // Press a button to make the gamepad visible to the page. + await service.newButtonEvent(buttonIndex, 0, true, true); + + const { length } = navigator.getGamepads(); + SimpleTest.is( + length, + 0, + "privacy.resistFingerprinting is true, navigator.getGamepads() should always return an empty array" + ); + + // Attempt to force gamepad events to be fired, by simulating gamepad disconnect + await service.removeGamepad(buttonIndex); + SimpleTest.finish(); +}); +</script> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_iframe.html b/browser/components/resistfingerprinting/test/mochitest/test_iframe.html new file mode 100644 index 0000000000..f01809b28c --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_iframe.html @@ -0,0 +1,18 @@ +<!doctype html> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +<body> +<script> + add_task(async function() { + await SpecialPowers.pushPrefEnv({ + "set": [["privacy.resistFingerprinting", true]], + }); + is(screen.width, window.innerWidth, "Width should be spoofed"); + is(screen.height, window.innerHeight, "Height should be spoofed"); + let iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + is(iframe.contentWindow.screen.width, iframe.contentWindow.innerWidth, "Width should be spoofed in iframe"); + is(iframe.contentWindow.screen.height, iframe.contentWindow.innerHeight, "Height should be spoofed in iframe"); + }); +</script> +</body> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_keyboard_event.html b/browser/components/resistfingerprinting/test/mochitest/test_keyboard_event.html new file mode 100644 index 0000000000..f2ef7107f8 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_keyboard_event.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1222285 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1222285</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1222285 **/ + SimpleTest.waitForExplicitFinish(); + + window.onload = () => { + SpecialPowers.pushPrefEnv({"set": + [ + ["privacy.resistFingerprinting", true], + ], + }, doTestForSystemEventGroup); + }; + + // This test makes sure that system event group will still get real keyboard event. + function doTestForSystemEventGroup() { + SpecialPowers.addSystemEventListener(document, "keydown", + function eventHandler(aEvent) { + SpecialPowers.removeSystemEventListener(document, + "keydown", eventHandler, true); + + is(aEvent.code, "Minus", "The system group event should get real code."); + is(aEvent.keyCode, 63, "The system group event should get real keyCode."); + + doTestModifiersForSystemEventGroup(); + }, true); + + // Send key event to the system group. + synthesizeKey("\u00DF", {code: "Minus", keyCode: 63}); + } + + // Test that will system group event still get suppressed modifier keys + function doTestModifiersForSystemEventGroup() { + SpecialPowers.addSystemEventListener(document, "keydown", + function eventHandler(aEvent) { + SpecialPowers.removeSystemEventListener(document, + "keydown", eventHandler, true); + is(aEvent.key, "Alt", "The system group event get the suppressed keyboard event."); + + SimpleTest.finish(); + }, true); + + // Send key event to the system group. + synthesizeKey("KEY_Alt", {altKey: true}); + } + + </script> +</head> +<body> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_pointer_event.html b/browser/components/resistfingerprinting/test/mochitest/test_pointer_event.html new file mode 100644 index 0000000000..49c83910b0 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_pointer_event.html @@ -0,0 +1,242 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1363508 +--> +<head> + <meta charset="utf-8"> + <title>Test for Pointer Events spoofing</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<div id="target0" style="width: 50px; height: 50px; background: green"></div> +<div id="target1" style="width: 50px; height: 50px; background: black"></div> +<script type="application/javascript"> + + /** Test for Bug 1363508 **/ + SimpleTest.waitForExplicitFinish(); + + var target0 = window.document.getElementById("target0"); + var target1 = window.document.getElementById("target1"); + var utils = SpecialPowers.Ci.nsIDOMWindowUtils; + + // A helper function to check that whether the pointer is spoofed correctly. + function checkPointerEvent(aEvent) { + is(aEvent.pointerId, utils.DEFAULT_MOUSE_POINTER_ID, + "The spoofed pointer event should always have the mouse pointer id."); + is(aEvent.width, 1, "The spoofed pointer event should always have width as 1."); + is(aEvent.height, 1, "The spoofed pointer event should always have width as 1."); + if (aEvent.buttons === 0) { + is(aEvent.pressure, 0.0, + "The spoofed pointer event should have pressure as 0.0 if it is not in a active buttons state."); + } else { + is(aEvent.pressure, 0.5, + "The spoofed pointer event should have pressure as 0.5 if it is in a active buttons state."); + } + is(aEvent.tangentialPressure, 0, "The spoofed pointer event should always have tangentialPressure as 0."); + is(aEvent.tiltX, 0, "The spoofed pointer event should always have tiltX as 0."); + is(aEvent.tiltY, 0, "The spoofed pointer event should always have tiltY as 0."); + is(aEvent.twist, 0, "The spoofed pointer event should always have twist as 0."); + is(aEvent.pointerType, "mouse", "The spoofed pointer event should always has mouse pointerType."); + is(aEvent.isPrimary, true, "The spoofed pointer event should only receive primary pointer events."); + } + + // A helper function to create a promise for waiting the event. + function promiseForEvent(aEventType, aCheckFunc) { + return new Promise(resolve => { + target0.addEventListener(aEventType, (event) => { + is(event.type, aEventType, "receive " + event.type + " on target0"); + aCheckFunc(event); + resolve(); + }, { once: true }); + }); + } + + // A test for pointer events from touch interface. + async function doTestForTouchPointerEvent() { + let eventPromises = [ + promiseForEvent("pointerover", checkPointerEvent), + promiseForEvent("pointerenter", checkPointerEvent), + promiseForEvent("pointerdown", checkPointerEvent), + promiseForEvent("pointermove", checkPointerEvent), + promiseForEvent("pointerup", checkPointerEvent), + promiseForEvent("pointerout", checkPointerEvent), + promiseForEvent("pointerleave", checkPointerEvent), + ]; + + synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 }); + synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 }); + synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 }); + + await Promise.all(eventPromises); + } + + // A test for pointercancel event. + async function doTestForTouchPointerCancelEvent() { + let eventPromises = [ + promiseForEvent("pointerover", checkPointerEvent), + promiseForEvent("pointerenter", checkPointerEvent), + promiseForEvent("pointerdown", checkPointerEvent), + promiseForEvent("pointermove", checkPointerEvent), + promiseForEvent("pointercancel", checkPointerEvent), + promiseForEvent("pointerout", checkPointerEvent), + promiseForEvent("pointerleave", checkPointerEvent), + ]; + + synthesizeTouch(target0, 5, 5, { type: "touchstart" }); + synthesizeTouch(target0, 6, 6, { type: "touchmove" }); + synthesizeTouch(target0, 6, 6, { type: "touchcancel" }); + + await Promise.all(eventPromises); + } + + // A test for pointer events from pen interface. + async function doTestForPenPointerEvent() { + let eventPromises = [ + promiseForEvent("pointerover", checkPointerEvent), + promiseForEvent("pointerenter", checkPointerEvent), + promiseForEvent("pointerdown", checkPointerEvent), + promiseForEvent("pointermove", checkPointerEvent), + promiseForEvent("pointerup", checkPointerEvent), + promiseForEvent("pointerout", checkPointerEvent), + promiseForEvent("pointerleave", checkPointerEvent), + ]; + + synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_PEN }); + synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_PEN }); + synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_PEN }); + synthesizeMouse(target1, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_PEN }); + + await Promise.all(eventPromises); + } + + // A test for gotpointercapture and lostpointercapture events. + // We would also test releasePointerCapture for only accepting spoofed pointer + // Id here. + async function doTestForPointerCapture() { + // We test for both mouse and touch to see whether the capture events are + // filed properly. We don't check pen here since it won't file capture + // events. + let inputSources = [ MouseEvent.MOZ_SOURCE_MOUSE, + MouseEvent.MOZ_SOURCE_TOUCH ]; + + for (let inputSource of inputSources) { + function eventHandler(event) { + checkPointerEvent(event); + if (event.type === "pointerdown") { + target0.setPointerCapture(event.pointerId); + } else if (event.type === "pointermove") { + if (inputSource === MouseEvent.MOZ_SOURCE_TOUCH) { + try { + target0.releasePointerCapture(utils.DEFAULT_TOUCH_POINTER_ID); + ok(false, "The releasePointerCapture should fail here, but it is not."); + } catch (e) { + ok(true, "The releasePointerCapture fails properly."); + } + } + target0.releasePointerCapture(event.pointerId); + } + } + + let eventPromises = [ + promiseForEvent("pointerover", eventHandler), + promiseForEvent("pointerenter", eventHandler), + promiseForEvent("pointerdown", eventHandler), + promiseForEvent("gotpointercapture", eventHandler), + promiseForEvent("pointermove", eventHandler), + promiseForEvent("lostpointercapture", eventHandler), + promiseForEvent("pointerup", eventHandler), + promiseForEvent("pointerout", eventHandler), + promiseForEvent("pointerleave", eventHandler), + ]; + + synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource }); + synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource }); + synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource }); + synthesizeMouse(target1, 5, 5, { type: "mousemove", inputSource }); + + await Promise.all(eventPromises); + } + } + + // A test for setPointerCapture() for only accepting spoofed pointer id. + async function doTestForSetPointerCapture() { + function eventHandler(event) { + checkPointerEvent(event); + if (event.type === "pointerdown") { + try { + target0.setPointerCapture(utils.DEFAULT_TOUCH_POINTER_ID); + ok(false, "The setPointerCapture should fail here, but it is not."); + } catch (e) { + ok(true, "The setPointerCapture fails properly."); + } + } + } + + let eventPromises = [ + promiseForEvent("pointerover", eventHandler), + promiseForEvent("pointerenter", eventHandler), + promiseForEvent("pointerdown", eventHandler), + promiseForEvent("pointermove", eventHandler), + promiseForEvent("pointerup", eventHandler), + promiseForEvent("pointerout", eventHandler), + promiseForEvent("pointerleave", eventHandler), + ]; + + synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_TOUCH }); + synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_TOUCH }); + synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_TOUCH }); + + await Promise.all(eventPromises); + } + + // A test for assuring that script generated events won't be spoofed. + function doTestNoSpoofingForScriptGeneratedEvent() { + return new Promise(resolve => { + // Generate a custom pointer event by script. + let pointerEventCustom = new PointerEvent("pointerover", { + pointerId: utils.DEFAULT_TOUCH_POINTER_ID, + pointerType: "touch", + width: 5, + height: 5, + pressure: 0.75, + tangentialPressure: 0.5, + isPrimary: false, + }); + + target0.addEventListener("pointerover", (event) => { + // Check that script generated event is not spoofed. + is(event.pointerType, "touch", "The pointerEvent.pointerType is not spoofed."); + is(event.width, 5, "The pointerEvent.width is not spoofed."); + is(event.height, 5, "The pointerEvent.height is not spoofed."); + is(event.pressure, 0.75, "The pointerEvent.pressure is not spoofed."); + is(event.tangentialPressure, 0.5, "The pointerEvent.tangentialPressure is not spoofed."); + is(event.isPrimary, false, "The pointerEvent.isPrimary is not spoofed."); + resolve(); + }, { once: true }); + + target0.dispatchEvent(pointerEventCustom); + }); + } + + async function doTests() { + await doTestForTouchPointerEvent(); + await doTestForTouchPointerCancelEvent(); + await doTestForPenPointerEvent(); + await doTestForPointerCapture(); + await doTestForSetPointerCapture(); + await doTestNoSpoofingForScriptGeneratedEvent(); + + SimpleTest.finish(); + } + + SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting", true]]}, + doTests); + }); + +</script> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/test_speech_synthesis.html b/browser/components/resistfingerprinting/test/mochitest/test_speech_synthesis.html new file mode 100644 index 0000000000..e8df16c665 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_speech_synthesis.html @@ -0,0 +1,105 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1333641 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1333641</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 1333641 **/ + SimpleTest.waitForExplicitFinish(); + window.onload = setupSpeechSynthesis; + + // This function setup the speechSynthesis and flip 'privacy.resistFingerprinting' + // after it has been setup correctly. + function setupSpeechSynthesis() { + window.speechSynthesis.addEventListener("voiceschanged", () => { + isnot(window.speechSynthesis.getVoices().length, 0, "Voices added"); + SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({"set": + [["privacy.resistFingerprinting", true]], + }, doGetVoicesTest); + }, window); + }, {once: true}); + + is(window.speechSynthesis.getVoices().length, 0, "No voices added initially"); + } + + function doGetVoicesTest() { + is(window.speechSynthesis.getVoices().length, 0, + "There should be no voices after fingerprinting resistance is enabled."); + doVoiceschangedEventTest(); + } + + function doVoiceschangedEventTest() { + window.speechSynthesis.addEventListener("voiceschanged", () => { + ok(false, "The voiceschanged event should not be fired."); + doSpeakTestAsync(); + }, {once: true}); + + window.addEventListener("TestEvent", () => { + // If we receive this event without receiving a 'voiceschanged' event, this means + // the voiceschanged event has been blocked correctly. + ok(true, "Got the 'TestEvent' event."); + doSpeakTestAsync(); + }, {once: true}); + + // Notify 'synth-voices-changed' for triggering the voiceschanged event. + SpecialPowers.Services.obs.notifyObservers(null, "synth-voices-changed"); + window.dispatchEvent(new CustomEvent("TestEvent")); + } + + // This tests Speak() and its asynchronousness. + function doSpeakTestAsync() { + // For non-e10s, this test will always fail since the event will be triggered immediately + // after speak() is called. So, new added events after speak() won't be called. We skip + // this test if it is non-e10s. + if (SpecialPowers.Services.appinfo.browserTabsRemoteAutostart) { + let utterance = new window.SpeechSynthesisUtterance("Hello, world!"); + window.speechSynthesis.speak(utterance); + + utterance.addEventListener("start", () => { + ok(false, "speechSynthesis should not start speaking if fingerprinting resistance is enabled."); + doSpeakTestSync(); + }, {once: true}); + + utterance.addEventListener("error", () => { + ok(true, "speechSynthesis.speak should fail if fingerprinting resistance is enabled."); + doSpeakTestSync(); + }, {once: true}); + } else { + doSpeakTestSync(); + } + } + + // This tests Speak() and its synchronousness. + function doSpeakTestSync() { + let utterance = new window.SpeechSynthesisUtterance("Hello, world!"); + utterance.addEventListener("start", () => { + ok(false, "speechSynthesis should not start speaking if fingerprinting resistance is enabled."); + SimpleTest.finish(); + }, {once: true}); + + utterance.addEventListener("error", () => { + ok(true, "speechSynthesis.speak should fail if fingerprinting resistance is enabled."); + SimpleTest.finish(); + }, {once: true}); + + window.speechSynthesis.speak(utterance); + } + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"></pre> +</body> +</html> diff --git a/browser/components/resistfingerprinting/test/mochitest/worker_child.js b/browser/components/resistfingerprinting/test/mochitest/worker_child.js new file mode 100644 index 0000000000..fa340fc652 --- /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); diff --git a/browser/components/resistfingerprinting/test/mochitest/worker_grandchild.js b/browser/components/resistfingerprinting/test/mochitest/worker_grandchild.js new file mode 100644 index 0000000000..cd21508b2b --- /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); +}); |