Bug 1511941 - Don't expose PerformanceNavigationTiming in RFP mode r=tjr,baku
authorsanketh <sgmenda@uwaterloo.ca>
Thu, 07 May 2020 16:33:38 +0000
changeset 528769 61463834bff63fd7cd1c65939aa40a72466bc566
parent 528767 67b2b97539bd1a13a68a84d8aee7b3f613a5985b
child 528770 8a48a3a488ab9f48ae4feaba11f0e5dab4cf00b9
push id37396
push userncsoregi@mozilla.com
push dateFri, 08 May 2020 15:58:04 +0000
treeherdermozilla-central@ac7a5cda729f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstjr, baku
bugs1511941
milestone78.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 1511941 - Don't expose PerformanceNavigationTiming in RFP mode r=tjr,baku In RFP mode, we do not support `PerformanceNavigationTiming`, so don't expose it. In particular, `window.PerformanceNavigationTiming` should return `undefined`. Added a new method `PerformanceNavigationTiming::Enabled` which when used with the WebIDL `Func` attribute allows us to toggle whether `window.PerformanceNavigationTiming` is exposed. Created `dom/tests/mochitest/general/test_toggling_performance_navigation_timing.html` to test whether the toggling works. Updated `browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js` to create a new window each time `privacy.resistFingerprinting` is flipped so this behavior does not leak into other tests. Differential Revision: https://phabricator.services.mozilla.com/D73528
browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
dom/performance/PerformanceMainThread.cpp
dom/performance/PerformanceNavigationTiming.cpp
dom/performance/PerformanceNavigationTiming.h
dom/tests/mochitest/general/mochitest.ini
dom/tests/mochitest/general/test_toggling_performance_navigation_timing.html
dom/webidl/PerformanceNavigationTiming.webidl
--- a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
@@ -73,33 +73,39 @@ let isRounded = (x, expectedPrecision) =
       " Fuzzy 2: " +
       Math.abs(rounded - x)
   );
 
   return false;
 };
 
 let setupTest = async function(
-  tab,
   resistFingerprinting,
   reduceTimerPrecision,
   expectedPrecision,
   runTests,
   workerCall
 ) {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["privacy.resistFingerprinting", resistFingerprinting],
       ["privacy.reduceTimerPrecision", reduceTimerPrecision],
       [
         "privacy.resistFingerprinting.reduceTimerPrecision.microseconds",
         expectedPrecision * 1000,
       ],
     ],
   });
+
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    win.gBrowser,
+    TEST_PATH + "file_dummy.html"
+  );
+
   // 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 100ms
   if (resistFingerprinting) {
     expectedPrecision = expectedPrecision < 100 ? 100 : expectedPrecision;
   }
   await SpecialPowers.spawn(
     tab.linkedBrowser,
     [
@@ -107,25 +113,21 @@ let setupTest = async function(
         list: PERFORMANCE_TIMINGS,
         precision: expectedPrecision,
         isRoundedFunc: isRounded.toString(),
         workerCall,
       },
     ],
     runTests
   );
+  await BrowserTestUtils.closeWindow(win);
 };
 // ================================================================================================
 // ================================================================================================
 add_task(async function runRPTests() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    TEST_PATH + "file_dummy.html"
-  );
-
   let runTests = async function(data) {
     let timerlist = data.list;
     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);
 
@@ -162,31 +164,24 @@ add_task(async function runRPTests() {
     );
     is(
       content.performance.getEntriesByName("Test", "mark").length,
       0,
       "For resistFingerprinting, there should be no entries for performance.getEntriesByName()"
     );
   };
 
-  await setupTest(tab, true, true, 100, runTests);
-  await setupTest(tab, true, false, 13, runTests);
-  await setupTest(tab, true, false, 0.13, runTests);
-
-  BrowserTestUtils.removeTab(tab);
+  await setupTest(true, true, 100, runTests);
+  await setupTest(true, false, 13, runTests);
+  await setupTest(true, false, 0.13, runTests);
 });
 
 // ================================================================================================
 // ================================================================================================
 add_task(async function runRTPTests() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    TEST_PATH + "file_dummy.html"
-  );
-
   let runTests = async function(data) {
     let timerlist = data.list;
     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);
 
@@ -212,16 +207,17 @@ add_task(async function runRTPTests() {
     content.performance.mark("Test-End");
     content.performance.measure("Test-Measure", "Test", "Test-End");
 
     // Check the entries for performance.getEntries/getEntriesByType/getEntriesByName.
     is(
       content.performance.getEntries().length,
       4,
       "For reduceTimerPrecision, there should be 4 entries for performance.getEntries()"
+      // PerformanceNavigationTiming, PerformanceMark, PerformanceMark, PerformanceMeasure
     );
     for (var i = 0; i < 4; i++) {
       let startTime = content.performance.getEntries()[i].startTime;
       let duration = content.performance.getEntries()[i].duration;
       ok(
         isRounded(startTime, expectedPrecision),
         "For reduceTimerPrecision(" +
           expectedPrecision +
@@ -250,21 +246,19 @@ add_task(async function runRTPTests() {
       1,
       "For reduceTimerPrecision, there should be 1 entry for performance.getEntriesByName()"
     );
     content.performance.clearMarks();
     content.performance.clearMeasures();
     content.performance.clearResourceTimings();
   };
 
-  await setupTest(tab, false, true, 100, runTests);
-  await setupTest(tab, false, true, 13, runTests);
-  await setupTest(tab, false, true, 0.13, runTests);
-
-  BrowserTestUtils.removeTab(tab);
+  await setupTest(false, true, 100, runTests);
+  await setupTest(false, true, 13, runTests);
+  await setupTest(false, true, 0.13, runTests);
 });
 
 // ================================================================================================
 // ================================================================================================
 let runWorkerTest = async function(data) {
   let expectedPrecision = data.precision;
   let workerCall = data.workerCall;
   await new Promise(resolve => {
@@ -281,32 +275,18 @@ let runWorkerTest = async function(data)
         resolve();
       }
     };
     worker.postMessage({ type: workerCall, precision: expectedPrecision });
   });
 };
 
 add_task(async function runRPTestsForWorker() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    TEST_PATH + "file_dummy.html"
-  );
-
-  await setupTest(tab, true, true, 100, runWorkerTest, "runRPTests");
-  await setupTest(tab, true, false, 13, runWorkerTest, "runRPTests");
-  await setupTest(tab, true, true, 0.13, runWorkerTest, "runRPTests");
-
-  BrowserTestUtils.removeTab(tab);
+  await setupTest(true, true, 100, runWorkerTest, "runRPTests");
+  await setupTest(true, false, 13, runWorkerTest, "runRPTests");
+  await setupTest(true, true, 0.13, runWorkerTest, "runRPTests");
 });
 
 add_task(async function runRTPTestsForWorker() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    TEST_PATH + "file_dummy.html"
-  );
-
-  await setupTest(tab, false, true, 100, runWorkerTest, "runRTPTests");
-  await setupTest(tab, false, true, 13, runWorkerTest, "runRTPTests");
-  await setupTest(tab, false, true, 0.13, runWorkerTest, "runRTPTests");
-
-  BrowserTestUtils.removeTab(tab);
+  await setupTest(false, true, 100, runWorkerTest, "runRTPTests");
+  await setupTest(false, true, 13, runWorkerTest, "runRTPTests");
+  await setupTest(false, true, 0.13, runWorkerTest, "runRTPTests");
 });
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PerformanceMainThread.h"
 #include "PerformanceNavigation.h"
 #include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_privacy.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 void GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec) {
   aSpec.AssignLiteral("document");
@@ -300,17 +301,18 @@ TimeStamp PerformanceMainThread::Creatio
 
 DOMHighResTimeStamp PerformanceMainThread::CreationTime() const {
   return GetDOMTiming()->GetNavigationStart();
 }
 
 void PerformanceMainThread::CreateNavigationTimingEntry() {
   MOZ_ASSERT(!mDocEntry, "mDocEntry should be null.");
 
-  if (!StaticPrefs::dom_enable_performance_navigation_timing()) {
+  if (!StaticPrefs::dom_enable_performance_navigation_timing() ||
+      StaticPrefs::privacy_resistFingerprinting()) {
     return;
   }
 
   nsAutoString name;
   GetURLSpecFromChannel(mChannel, name);
 
   UniquePtr<PerformanceTimingData> timing(
       new PerformanceTimingData(mChannel, nullptr, 0));
--- a/dom/performance/PerformanceNavigationTiming.cpp
+++ b/dom/performance/PerformanceNavigationTiming.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/PerformanceNavigationTiming.h"
 #include "mozilla/dom/PerformanceNavigationTimingBinding.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_privacy.h"
 
 using namespace mozilla::dom;
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceNavigationTiming)
 NS_INTERFACE_MAP_END_INHERITING(PerformanceResourceTiming)
 
 NS_IMPL_ADDREF_INHERITED(PerformanceNavigationTiming, PerformanceResourceTiming)
 NS_IMPL_RELEASE_INHERITED(PerformanceNavigationTiming,
@@ -131,8 +133,13 @@ NavigationType PerformanceNavigationTimi
 uint16_t PerformanceNavigationTiming::RedirectCount() const {
   return mTimingData->GetRedirectCount();
 }
 
 void PerformanceNavigationTiming::UpdatePropertiesFromHttpChannel(
     nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel) {
   mTimingData->SetPropertiesFromHttpChannel(aHttpChannel, aChannel);
 }
+
+bool PerformanceNavigationTiming::Enabled(JSContext* aCx, JSObject* aGlobal) {
+  return (StaticPrefs::dom_enable_performance_navigation_timing() &&
+          !StaticPrefs::privacy_resistFingerprinting());
+}
--- a/dom/performance/PerformanceNavigationTiming.h
+++ b/dom/performance/PerformanceNavigationTiming.h
@@ -54,16 +54,22 @@ class PerformanceNavigationTiming final 
   DOMHighResTimeStamp LoadEventStart() const;
   DOMHighResTimeStamp LoadEventEnd() const;
   NavigationType Type() const;
   uint16_t RedirectCount() const;
 
   void UpdatePropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel,
                                        nsITimedChannel* aChannel);
 
+  /*
+   * For use with the WebIDL Func attribute to determine whether
+   * window.PerformanceNavigationTiming is exposed.
+   */
+  static bool Enabled(JSContext* aCx, JSObject* aGlobal);
+
  private:
   ~PerformanceNavigationTiming() = default;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_PerformanceNavigationTiming_h___
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -122,15 +122,16 @@ skip-if = verify
 [test_resource_timing_frameset.html]
 [test_selectevents.html]
 [test_showModalDialog_removed.html]
 [test_storagePermissionsAccept.html]
 [test_storagePermissionsLimitForeign.html]
 [test_storagePermissionsReject.html]
 [test_storagePermissionsRejectForeign.html]
 [test_stylesheetPI.html]
+[test_toggling_performance_navigation_timing.html]
 [test_vibrator.html]
 [test_WebKitCSSMatrix.html]
 [test_windowedhistoryframes.html]
 [test_windowProperties.html]
 [test_resource_timing_nocors.html]
 [test_resizeby.html]
 skip-if = (toolkit == 'android') || (devedition && os == 'win' && bits == 32) || (os == 'linux' && bits == 64) # Window sizes cannot be controled on android; Windows: bug 1540554; Bug 1604152
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_toggling_performance_navigation_timing.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1511941 - Don't expose PerformanceNavigationTiming when it is disabled</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <div id="content"> </div>
+  <script type="application/javascript">
+    async function testWhetherExposed(resistFingerprinting, enable_performance_navigation_timing) {
+      await SpecialPowers.pushPrefEnv({
+        "set": [["privacy.resistFingerprinting", resistFingerprinting],
+                ["dom.enable_performance_navigation_timing", enable_performance_navigation_timing]],
+        });
+      var iframe = document.createElement("iframe");
+      document.body.append(iframe);
+      var p = iframe.contentWindow.PerformanceNavigationTiming;
+      if (resistFingerprinting)
+        is(p, undefined, "window.PerformanceNavigationTiming should not be exposed when"
+                         + " dom.enable_performance_navigation_timing=" + enable_performance_navigation_timing
+                         + " and privacy.resistFingerprinting="+ resistFingerprinting +".");
+      if (!enable_performance_navigation_timing)
+        is(p, undefined, "window.PerformanceNavigationTiming should not be exposed when"
+                         + " dom.enable_performance_navigation_timing=" + enable_performance_navigation_timing
+                         + " and privacy.resistFingerprinting="+ resistFingerprinting +".");
+      if (enable_performance_navigation_timing && !resistFingerprinting) {
+        isnot(p, undefined, "window.PerformanceNavigationTiming should be exposed when"
+                            + " dom.enable_performance_navigation_timing=" + enable_performance_navigation_timing
+                            + " and privacy.resistFingerprinting="+ resistFingerprinting +".");
+      }
+    }
+
+    async function start() {
+      await testWhetherExposed(true,true);
+      await testWhetherExposed(true,false);
+      await testWhetherExposed(false,true);
+      await testWhetherExposed(false,false);
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    start();
+  </script>
+</body>
+</html>
--- a/dom/webidl/PerformanceNavigationTiming.webidl
+++ b/dom/webidl/PerformanceNavigationTiming.webidl
@@ -12,17 +12,18 @@
 
 enum NavigationType {
   "navigate",
   "reload",
   "back_forward",
   "prerender"
 };
 
-[Exposed=Window]
+[Exposed=Window,
+ Func="mozilla::dom::PerformanceNavigationTiming::Enabled"]
 interface PerformanceNavigationTiming : PerformanceResourceTiming {
   readonly        attribute DOMHighResTimeStamp unloadEventStart;
   readonly        attribute DOMHighResTimeStamp unloadEventEnd;
   readonly        attribute DOMHighResTimeStamp domInteractive;
   readonly        attribute DOMHighResTimeStamp domContentLoadedEventStart;
   readonly        attribute DOMHighResTimeStamp domContentLoadedEventEnd;
   readonly        attribute DOMHighResTimeStamp domComplete;
   readonly        attribute DOMHighResTimeStamp loadEventStart;