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
--- 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,