/** * Bug 1621677 - A test for making sure getting the correct (higher) precision * when it's cross-origin-isolated. */ const TEST_SCENARIO_1 = 1; const TEST_SCENARIO_2 = 2; const TEST_SCENARIO_3 = 3; const TEST_SCENARIO_4 = 4; const TEST_SCENARIO_5 = 5; const TEST_SCENARIO_6 = 6; const TEST_SCENARIO_7 = 7; const TEST_SCENARIO_8 = 8; const TEST_SCENARIO_9 = 9; const TEST_SCENARIO_10 = 10; const TEST_SCENARIO_11 = 11; const TEST_SCENARIO_101 = 101; const TEST_SCENARIO_102 = 102; const TEST_SCENARIO_103 = 103; const TEST_SCENARIO_104 = 104; const TEST_SCENARIO_105 = 105; const TEST_SCENARIO_106 = 106; const TEST_SCENARIO_107 = 107; const TEST_SCENARIO_108 = 108; const TEST_SCENARIO_109 = 109; const TEST_SCENARIO_110 = 110; const TEST_SCENARIO_111 = 111; requestLongerTimeout(2); let processResultsGlobal = (data, successes, failures) => { let expectedPrecision = data.precision; let scenario = data.options.scenario; let shouldBeRounded = data.options.shouldBeRounded; for (let success of successes) { ok( true, (shouldBeRounded ? "Should " : "Should not ") + `have rounded '${success[0]}' to nearest ${expectedPrecision} ms; saw ${success[1]}. ` + `scenario: TEST_SCENARIO_${scenario}` ); } if (failures.length > 2) { for (let failure of failures) { ok( false, (shouldBeRounded ? "Should " : "Should not ") + `have rounded '${failure[0]}' to nearest ${expectedPrecision} ms; saw ${failure[1]}. ` + `scenario: TEST_SCENARIO_${scenario}` ); } } else if ( failures.length == 2 && expectedPrecision < 10 && failures[0][0].indexOf("Date().getTime()") > 0 && failures[1][0].indexOf('File([], "").lastModified') > 0 ) { /* * At high precisions, the epoch-based timestamps are large enough that their expected * rounding values lie directly between two integers; and floating point math is imprecise enough * that we need to accept these failures */ ok( true, "Two Free Failures that " + (data.options.shouldBeRounded ? "ahould " : "should not ") + `be rounded on the epoch dates and precision: ${expectedPrecision}. ` + `scenario: TEST_SCENARIO_${data.options.scenario}` ); } else if (failures.length == 1) { ok( true, "Free Failure: " + (data.options.shouldBeRounded ? "Should " : "Should not ") + `have rounded '${failures[0][0]}' to nearest ${expectedPrecision} ms; saw ${failures[0][1]}. ` + `scenario: TEST_SCENARIO_${data.options.scenario}` ); } }; // ================================================================================================ // ================================================================================================ // This test case is mostly copy-and-paste from the test case for window in // test_reduce_time_precision.html. The main difference is this test case // verifies DOM API has more precsion when it's in cross-origin-isolated and // cross-origin-isolated doesn't affect RFP. add_task(async function runRTPTestDOM() { let runTests = async function (data) { let expectedPrecision = data.precision; // eslint beleives that isrounded is available in this scope, but if you // remove the assignment, you will see it is not // eslint-disable-next-line let isRounded = eval(data.isRoundedFunc); // eslint-disable-next-line let processResults = eval(data.options.processResultsFunc); // Prepare for test of AudioContext.currentTime // eslint-disable-next-line let audioContext = new content.AudioContext(); // Known ways to generate time stamps, in milliseconds const timeStampCodes = [ "new content.Date().getTime()", 'new content.File([], "").lastModified', "content.performance.now()", 'new content.Event("").timeStamp', ]; // These are measured in seconds, so we need to scale them up var timeStampCodesDOM = timeStampCodes.concat([ "audioContext.currentTime * 1000", ]); // If we are not rounding values, this function will invert the return value let resultSwitchisRounded = function (timeStamp) { if (timeStamp == 0) { return true; } let result = isRounded(timeStamp, expectedPrecision, content.console); return data.options.shouldBeRounded ? result : !result; }; // It's possible that even if we shouldn't be rounding something, we get a timestamp that is rounded. // If we have only one of these outliers, we are okay with that. This should significantly // reduce intermittents, although it's still likely there will be some intermittents. We can increase // this if we need to let successes = [], failures = []; // Loop through each timeStampCode, evaluate it, // and check if it is rounded for (let timeStampCode of timeStampCodesDOM) { // eslint-disable-next-line let timeStamp = eval(timeStampCode); // Audio Contexts increment in intervals of (minimum) 5.4ms, so we don't // clamp/jitter if the timer precision is les than that. // (Technically on MBPs they increment in intervals of 2.6 but this is // non-standard and will eventually be changed. We don't cover this situation // because we don't really support arbitrary Timer Precision, especially in // the 2.6 - 5.4ms interval.) if (timeStampCode.includes("audioContext") && expectedPrecision < 5.4) { continue; } if (timeStamp != 0 && resultSwitchisRounded(timeStamp)) { successes = successes.concat([[timeStampCode, timeStamp]]); } else if (timeStamp != 0) { failures = failures.concat([[timeStampCode, timeStamp]]); } } processResults(data, successes, failures); }; // RFP await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_1, processResultsFunc: processResultsGlobal.toString(), }, 100, runTests ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, reduceTimerPrecision: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_2, processResultsFunc: processResultsGlobal.toString(), }, 100, runTests ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_3, processResultsFunc: processResultsGlobal.toString(), }, 100, runTests ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_4, processResultsFunc: processResultsGlobal.toString(), }, 13, runTests ); /* Disabled because it causes too many intermittents await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_5, processResultsFunc: processResultsGlobal.toString(), }, 13, runTests ); */ await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_6, processResultsFunc: processResultsGlobal.toString(), }, 13, runTests ); // We cannot run the tests with too fine a precision, or it becomes very likely // to get false results that a number 'should not been rounded', when it really // wasn't, we had just gotten an accidental match await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_7, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runTests ); /* Disabled because it causes too many intermittents await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_8, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runTests ); */ await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_9, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runTests ); // RTP await setupAndRunCrossOriginIsolatedTest( { reduceTimerPrecision: true, scenario: TEST_SCENARIO_10, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runTests ); await setupAndRunCrossOriginIsolatedTest( { reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_11, processResultsFunc: processResultsGlobal.toString(), }, 0.005, runTests ); }); // ================================================================================================ // ================================================================================================ // This test case is mostly copy-and-paste from the test case for worker in // test_reduce_time_precision.html. The main difference is this test case // verifies DOM API has more precsion when it's in cross-origin-isolated and // cross-origin-isolated doesn't affect RFP. let runWorkerTest = async function (data) { let expectedPrecision = data.precision; await new Promise(resolve => { // eslint beleives that isrounded is available in this scope, but if you // remove the assignment, you will see it is not // eslint-disable-next-line let isRounded = eval(data.isRoundedFunc); // eslint-disable-next-line let processResults = eval(data.options.processResultsFunc); let worker = new content.Worker( "coop_header.sjs?crossOriginIsolated=true&worker=true" ); // Known ways to generate time stamps, in milliseconds const timeStampCodes = [ "new Date().getTime()", 'new File([], "").lastModified', "performance.now()", 'new Event("").timeStamp', ]; let promises = [], successes = [], failures = []; for (let timeStampCode of timeStampCodes) { promises.push( new Promise(res => { worker.postMessage({ type: "runCmdAndGetResult", cmd: timeStampCode, }); worker.addEventListener("message", function (e) { // If we are not rounding values, this function will invert the return value let resultSwitchisRounded = function (timeStamp) { if (timeStamp == 0) { return true; } let result = isRounded(timeStamp, expectedPrecision); return data.options.shouldBeRounded ? result : !result; }; if (e.data.type == "result") { if (e.data.resultOf == timeStampCode) { if (resultSwitchisRounded(e.data.result)) { successes = successes.concat([ [timeStampCode, e.data.result], ]); } else { failures = failures.concat([[timeStampCode, e.data.result]]); } worker.removeEventListener("message", this); res(); } return; } ok(false, `Unknown message type. Got ${e.data.type}`); res(); }); }) ); } Promise.all(promises).then(_ => { worker.terminate(); processResults(data, successes, failures); resolve(); }); }); }; add_task(async function runRTPTestsForWorker() { // RFP await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_101, processResultsFunc: processResultsGlobal.toString(), }, 100, runWorkerTest ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, reduceTimerPrecision: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_102, processResultsFunc: processResultsGlobal.toString(), }, 100, runWorkerTest ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_103, processResultsFunc: processResultsGlobal.toString(), }, 100, runWorkerTest ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_104, processResultsFunc: processResultsGlobal.toString(), }, 13, runWorkerTest ); /* Disabled because it causes too many intermittents await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_105, processResultsFunc: processResultsGlobal.toString(), }, 13, runWorkerTest ); */ await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_106, processResultsFunc: processResultsGlobal.toString(), }, 13, runWorkerTest ); await setupAndRunCrossOriginIsolatedTest( { resistFingerprinting: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_107, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runWorkerTest ); /* Disabled because it causes too many intermittents await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, crossOriginIsolated: true, shouldBeRounded: false, scenario: TEST_SCENARIO_108, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runWorkerTest ); */ await setupAndRunCrossOriginIsolatedTest( { resistFingerprintingPBMOnly: true, openPrivateWindow: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_109, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runWorkerTest ); // RTP await setupAndRunCrossOriginIsolatedTest( { reduceTimerPrecision: true, scenario: TEST_SCENARIO_110, processResultsFunc: processResultsGlobal.toString(), }, 7.97, runWorkerTest ); await setupAndRunCrossOriginIsolatedTest( { reduceTimerPrecision: true, crossOriginIsolated: true, scenario: TEST_SCENARIO_111, processResultsFunc: processResultsGlobal.toString(), }, 0.005, runWorkerTest ); });