Bug 1425462 Normalize the JavaScript Engine behavior by adding a callback r=luke
☠☠ backed out by eec4330cfdb3 ☠ ☠
authorTom Ritter <tom@mozilla.com>
Thu, 22 Feb 2018 16:05:50 -0600
changeset 461167 127b5d2e6779a4e94f7e939210a99b12e0c32f5f
parent 461166 95ce64d3a29a9a50de86bf713b69f43b8e05230c
child 461168 85896ea96faf2f55b4401674b1c469c7d9be513d
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1425462, 1440539
milestone60.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 1425462 Normalize the JavaScript Engine behavior by adding a callback r=luke Time Precision Reduction in the JS Engine was handled by a small bit of duplicated logic. With Time Jittering, and general improvements to the logic due to float fuzziness, we want to unify the logic for the JS Engine and the browser into one location. This patch does that. Note that this will leave the JS Shell without a time jittering implementation. It currently has a time clamping implementation - but I'm not actually sure if the shell is doing anything with it, because it's probably not calling SetTimeResolutionUsec to set it up. In Bug 1440539 we will add a jitter implementation for the shell. (And probably turn time rounding and jittering on for it too.) MozReview-Commit-ID: 2BTIMzE8MjW
js/public/Date.h
js/src/jsdate.cpp
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -166,16 +166,27 @@ DayFromYear(double year);
 
 // Takes an integer number of milliseconds since the epoch and an integer year,
 // returns the number of days in that year. If |time| is nonfinite, returns NaN.
 // Otherwise |time| *must* correspond to a time within the valid year |year|.
 // This should usually be ensured by computing |year| as |JS::DayFromYear(time)|.
 JS_PUBLIC_API(double)
 DayWithinYear(double time, double year);
 
-// Sets the time resolution for fingerprinting protection.
-// If it's set to zero, then no rounding will happen.
+// The callback will be a wrapper function that accepts a single double (the time
+// to clamp and jitter.) Inside the JS Engine, other parameters that may be needed
+// are all constant, so they are handled inside the wrapper function
+using ReduceMicrosecondTimePrecisionCallback = double(*)(double);
+
+// Set a callback into the toolkit/components/resistfingerprinting function that
+// will centralize time resolution and jitter into one place.
+JS_PUBLIC_API(void)
+SetReduceMicrosecondTimePrecisionCallback(ReduceMicrosecondTimePrecisionCallback callback);
+
+// Sets the time resolution for fingerprinting protection, and whether jitter
+// should occur. If resolution is set to zero, then no rounding or jitter will
+// occur. This is used if the callback above is not specified.
 JS_PUBLIC_API(void)
 SetTimeResolutionUsec(uint32_t resolution, bool jitter);
 
 } // namespace JS
 
 #endif /* js_Date_h */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -60,16 +60,19 @@ using JS::ClippedTime;
 using JS::GenericNaN;
 using JS::TimeClip;
 using JS::ToInteger;
 
 // When this value is non-zero, we'll round the time by this resolution.
 static Atomic<uint32_t, Relaxed> sResolutionUsec;
 // This is not implemented yet, but we will use this to know to jitter the time in the JS shell
 static Atomic<bool, Relaxed> sJitter;
+// The callback we will use for the Gecko implementation of Timer Clamping/Jittering
+static Atomic<JS::ReduceMicrosecondTimePrecisionCallback, Relaxed> sReduceMicrosecondTimePrecisionCallback;
+
 
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is a script:
  *
  *    today = new Date();
  *
  *    print(today.toLocaleString());
@@ -402,16 +405,22 @@ JS::DayFromYear(double year)
 
 JS_PUBLIC_API(double)
 JS::DayWithinYear(double time, double year)
 {
     return ::DayWithinYear(time, year);
 }
 
 JS_PUBLIC_API(void)
+JS::SetReduceMicrosecondTimePrecisionCallback(JS::ReduceMicrosecondTimePrecisionCallback callback)
+{
+    sReduceMicrosecondTimePrecisionCallback = callback;
+}
+
+JS_PUBLIC_API(void)
 JS::SetTimeResolutionUsec(uint32_t resolution, bool jitter)
 {
     sResolutionUsec = resolution;
     sJitter = jitter;
 }
 
 /*
  * Find a year for which any given date will fall on the same weekday.
@@ -1294,19 +1303,21 @@ date_parse(JSContext* cx, unsigned argc,
     args.rval().set(TimeValue(result));
     return true;
 }
 
 static ClippedTime
 NowAsMillis()
 {
     double now = PRMJ_Now();
-    if (sResolutionUsec) {
+    if (sReduceMicrosecondTimePrecisionCallback)
+        now = sReduceMicrosecondTimePrecisionCallback(now);
+    else if (sResolutionUsec)
         now = floor(now / sResolutionUsec) * sResolutionUsec;
-    }
+
     return TimeClip(now / PRMJ_USEC_PER_MSEC);
 }
 
 bool
 js::date_now(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(TimeValue(NowAsMillis()));
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -473,16 +473,23 @@ nsRFPService::ReduceTimePrecisionImpl(
 double
 nsRFPService::ReduceTimePrecisionAsUSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
 {
   return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), aType);
 }
 
 /* static */
 double
+nsRFPService::ReduceTimePrecisionAsUSecsWrapper(double aTime)
+{
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), TimerPrecisionType::All);
+}
+
+/* static */
+double
 nsRFPService::ReduceTimePrecisionAsMSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
 {
   return nsRFPService::ReduceTimePrecisionImpl(aTime, MilliSeconds, TimerResolution(), aType);
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
@@ -666,16 +673,17 @@ nsRFPService::Init()
 
 // This function updates only timing-related fingerprinting items
 void
 nsRFPService::UpdateTimers() {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sPrivacyResistFingerprinting || sPrivacyTimerPrecisionReduction) {
     JS::SetTimeResolutionUsec(TimerResolution(), sJitter);
+    JS::SetReduceMicrosecondTimePrecisionCallback(nsRFPService::ReduceTimePrecisionAsUSecsWrapper);
   } else if (sInitialized) {
     JS::SetTimeResolutionUsec(0, false);
   }
 }
 
 
 // This function updates every fingerprinting item necessary except timing-related
 void
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -175,16 +175,20 @@ public:
     TimerPrecisionType aType = TimerPrecisionType::All);
   static double ReduceTimePrecisionAsMSecs(
     double aTime,
     TimerPrecisionType aType = TimerPrecisionType::All);
   static double ReduceTimePrecisionAsSecs(
     double aTime,
     TimerPrecisionType aType = TimerPrecisionType::All);
 
+  // Used by the JS Engine, as it doesn't know about the TimerPrecisionType enum
+  static double ReduceTimePrecisionAsUSecsWrapper(
+    double aTime);
+
   // Public only for testing purposes
   static double ReduceTimePrecisionImpl(
     double aTime,
     TimeScale aTimeScale,
     double aResolutionUSec,
     TimerPrecisionType aType);
   static nsresult RandomMidpoint(long long aClampedTimeUSec,
                                  long long aResolutionUSec,