Bug 1430173 Reduce the precision of all explicit clocks to 2ms r?baku draft
authorTom Ritter <tom@mozilla.com>
Tue, 20 Feb 2018 12:18:30 -0600
changeset 760145 ea03210ae93f8ecd7cd73966968596005d7f9558
parent 757440 62808e3ff95652ac3a392f91e848868d975978d8
child 760146 45dc916c551ca46c2cc82350437aebf281251f90
push id100551
push userbmo:tom@mozilla.com
push dateMon, 26 Feb 2018 22:12:44 +0000
reviewersbaku
bugs1430173
milestone52.6.1
Bug 1430173 Reduce the precision of all explicit clocks to 2ms r?baku Backport to ESR where we don't have the ResistFingerprinting component. MozReview-Commit-ID: 9bjycHjR3SF
dom/base/File.cpp
dom/base/MultipartBlobImpl.cpp
dom/base/TimerClamping.cpp
dom/base/TimerClamping.h
dom/base/moz.build
dom/console/Console.cpp
dom/events/Event.cpp
dom/events/Event.h
dom/media/DOMMediaStream.cpp
dom/media/webaudio/AudioContext.cpp
dom/performance/Performance.cpp
dom/performance/PerformanceTiming.cpp
dom/performance/PerformanceTiming.h
js/src/jsdate.cpp
old mode 100644
new mode 100755
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -24,16 +24,17 @@
 #include "nsIRemoteBlob.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIUUIDGenerator.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsStringStream.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
+#include "mozilla/TimerClamping.h"
 #include "mozilla/SHA1.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/FileSystemUtils.h"
@@ -722,17 +723,17 @@ BlobImplBase::GetType(nsAString& aType)
 int64_t
 BlobImplBase::GetLastModified(ErrorResult& aRv)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   if (IsDateUnknown()) {
     mLastModificationDate = PR_Now();
   }
 
-  return mLastModificationDate / PR_USEC_PER_MSEC;
+  return TimerClamping::ReduceUsTimeValue(mLastModificationDate) / PR_USEC_PER_MSEC;
 }
 
 void
 BlobImplBase::SetLastModified(int64_t aLastModified)
 {
   mLastModificationDate = aLastModified * PR_USEC_PER_MSEC;
 }
 
old mode 100644
new mode 100755
--- a/dom/base/MultipartBlobImpl.cpp
+++ b/dom/base/MultipartBlobImpl.cpp
@@ -12,16 +12,17 @@
 #include "nsDOMClassInfoID.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsStringStream.h"
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIXPConnect.h"
+#include "mozilla/TimerClamping.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS_INHERITED0(MultipartBlobImpl, BlobImpl)
 
 /* static */ already_AddRefed<MultipartBlobImpl>
@@ -265,18 +266,17 @@ MultipartBlobImpl::SetLengthAndModifiedD
 
   mLength = totalLength;
 
   if (mIsFile) {
     // We cannot use PR_Now() because bug 493756 and, for this reason:
     //   var x = new Date(); var f = new File(...);
     //   x.getTime() < f.dateModified.getTime()
     // could fail.
-    mLastModificationDate =
-      lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now();
+    mLastModificationDate = TimerClamping::ReduceUsTimeValue(lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now());
   }
 }
 
 void
 MultipartBlobImpl::GetMozFullPathInternal(nsAString& aFilename,
                                           ErrorResult& aRv) const
 {
   if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
new file mode 100755
--- /dev/null
+++ b/dom/base/TimerClamping.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "TimerClamping.h"
+
+namespace mozilla {
+
+/* static */
+double
+TimerClamping::ReduceSTimeValue(double aTime)
+{
+  static const double maxResolutionS = .002;
+  return floor(aTime / maxResolutionS) * maxResolutionS;
+}
+
+/* static */
+double
+TimerClamping::ReduceMsTimeValue(double aTime)
+{
+  static const double maxResolutionMs = 2;
+  return floor(aTime / maxResolutionMs) * maxResolutionMs;
+}
+
+/* static */
+double
+TimerClamping::ReduceUsTimeValue(double aTime)
+{
+  static const double maxResolutionUs = 2000;
+  return floor(aTime / maxResolutionUs) * maxResolutionUs;
+}
+
+}
\ No newline at end of file
new file mode 100755
--- /dev/null
+++ b/dom/base/TimerClamping.h
@@ -0,0 +1,22 @@
+/* -*- 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/. */
+
+#ifndef TimerClamping_h___
+#define TimerClamping_h___
+
+namespace mozilla {
+
+class TimerClamping
+{
+public:
+  static double ReduceSTimeValue(double aTime);
+  static double ReduceMsTimeValue(double aTime);
+  static double ReduceUsTimeValue(double aTime);
+};
+
+}
+
+#endif /* TimerClamping_h___ */
\ No newline at end of file
old mode 100644
new mode 100755
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -138,16 +138,17 @@ if CONFIG['MOZ_WEBRTC']:
         'nsDOMDataChannel.h',
         'nsDOMDataChannelDeclarations.h',
     ]
 
 EXPORTS.mozilla += [
     'CORSMode.h',
     'FeedWriterEnabled.h',
     'TextInputProcessor.h',
+    'TimerClamping.h',
     'UseCounter.h',
 ]
 
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
     'AnonymousContent.h',
     'Attr.h',
     'BarProps.h',
@@ -358,16 +359,17 @@ UNIFIED_SOURCES += [
     'StructuredCloneHolder.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'TabGroup.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'Timeout.cpp',
+    'TimerClamping.cpp',
     'TreeWalker.cpp',
     'WebKitCSSMatrix.cpp',
     'WebSocket.cpp',
     'WindowNamedPropertiesHandler.cpp',
     'WindowOrientationObserver.cpp',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
old mode 100644
new mode 100755
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -25,16 +25,17 @@
 #include "ScriptSettings.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "xpcpublic.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsProxyRelease.h"
+#include "mozilla/TimerClamping.h"
 #include "mozilla/ConsoleTimelineMarker.h"
 #include "mozilla/TimestampTimelineMarker.h"
 
 #include "nsIConsoleAPIStorage.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsIProgrammingLanguage.h"
@@ -1333,17 +1334,17 @@ Console::MethodInternal(JSContext* aCx, 
       }
     } else {
       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(workerPrivate);
 
       TimeDuration duration =
         mozilla::TimeStamp::Now() - workerPrivate->NowBaseTimeStamp();
 
-      monotonicTimer = duration.ToMilliseconds();
+      monotonicTimer = TimerClamping::ReduceMsTimeValue(duration.ToMilliseconds());
     }
   }
 
   if (aMethodName == MethodTime && !aData.IsEmpty()) {
     callData->mStartTimerStatus = StartTimer(aCx, aData[0],
                                              monotonicTimer,
                                              callData->mStartTimerLabel,
                                              &callData->mStartTimerValue);
old mode 100644
new mode 100755
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -27,16 +27,17 @@
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsJSEnvironment.h"
 #include "nsLayoutUtils.h"
 #include "nsPIWindowRoot.h"
+#include "mozilla/TimerClamping.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 extern bool IsCurrentThreadRunningChromeWorker();
 } // namespace workers
@@ -1080,16 +1081,22 @@ Event::DefaultPrevented(JSContext* aCx) 
   // i.e., preventDefault() has been called by chrome, return true only when
   // this is called by chrome.
   return mEvent->DefaultPreventedByContent() || IsChrome(aCx);
 }
 
 double
 Event::TimeStamp() const
 {
+  return TimerClamping::ReduceMsTimeValue(TimeStampImpl());
+}
+
+double
+Event::TimeStampImpl() const
+{
   if (!sReturnHighResTimeStamp) {
     return static_cast<double>(mEvent->mTime);
   }
 
   if (mEvent->mTimeStamp.IsNull()) {
     return 0.0;
   }
 
old mode 100644
new mode 100755
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -57,16 +57,17 @@ public:
 
 protected:
   virtual ~Event();
 
 private:
   void ConstructorInit(EventTarget* aOwner,
                        nsPresContext* aPresContext,
                        WidgetEvent* aEvent);
+  double TimeStampImpl() const;
 
 public:
   static Event* FromSupports(nsISupports* aSupports)
   {
     nsIDOMEvent* event =
       static_cast<nsIDOMEvent*>(aSupports);
 #ifdef DEBUG
     {
old mode 100644
new mode 100755
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -4,16 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMMediaStream.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIScriptError.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"
+#include "mozilla/TimerClamping.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackEvent.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
 #include "mozilla/dom/AudioNode.h"
 #include "AudioChannelAgent.h"
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/VideoTrack.h"
@@ -539,18 +540,18 @@ DOMMediaStream::Constructor(const Global
 }
 
 double
 DOMMediaStream::CurrentTime()
 {
   if (!mPlaybackStream) {
     return 0.0;
   }
-  return mPlaybackStream->
-    StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime);
+  return TimerClamping::ReduceSTimeValue(mPlaybackStream->
+    StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime));
 }
 
 void
 DOMMediaStream::GetId(nsAString& aID) const
 {
   aID = mID;
 }
 
old mode 100644
new mode 100755
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -36,16 +36,17 @@
 #include "MediaStreamAudioDestinationNode.h"
 #include "MediaStreamAudioSourceNode.h"
 #include "MediaStreamGraph.h"
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
+#include "mozilla/TimerClamping.h"
 #include "OscillatorNode.h"
 #include "PannerNode.h"
 #include "PeriodicWave.h"
 #include "ScriptProcessorNode.h"
 #include "StereoPannerNode.h"
 #include "WaveShaperNode.h"
 
 namespace mozilla {
@@ -739,17 +740,17 @@ AudioContext::DestinationStream() const
   }
   return nullptr;
 }
 
 double
 AudioContext::CurrentTime() const
 {
   MediaStream* stream = Destination()->Stream();
-  return stream->StreamTimeToSeconds(stream->GetCurrentTime());
+  return TimerClamping::ReduceSTimeValue(stream->StreamTimeToSeconds(stream->GetCurrentTime()));
 }
 
 void
 AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
   if (!mIsOffline) {
old mode 100644
new mode 100755
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -16,16 +16,17 @@
 #include "PerformanceWorker.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/PerformanceBinding.h"
 #include "mozilla/dom/PerformanceEntryEvent.h"
 #include "mozilla/dom/PerformanceNavigationBinding.h"
 #include "mozilla/dom/PerformanceObserverBinding.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TimerClamping.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #ifdef MOZ_WIDGET_GONK
 #define PERFLOG(msg, ...)  __android_log_print(ANDROID_LOG_INFO, "PerformanceTiming", msg, ##__VA_ARGS__)
 #else
 #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
 #endif
@@ -223,19 +224,19 @@ Performance::ClearResourceTimings()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mResourceEntries.Clear();
 }
 
 DOMHighResTimeStamp
 Performance::RoundTime(double aTime) const
 {
-  // Round down to the nearest 20us, because if the timer is too accurate people
+  // Round down to the nearest 2ms, because if the timer is too accurate people
   // can do nasty timing attacks with it.
-  const double maxResolutionMs = 0.020;
+  const double maxResolutionMs = 2;
   return floor(aTime / maxResolutionMs) * maxResolutionMs;
 }
 
 
 void
 Performance::Mark(const nsAString& aName, ErrorResult& aRv)
 {
   // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
old mode 100644
new mode 100755
--- a/dom/performance/PerformanceTiming.cpp
+++ b/dom/performance/PerformanceTiming.cpp
@@ -16,17 +16,17 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Per
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)
 
 PerformanceTiming::PerformanceTiming(Performance* aPerformance,
                                      nsITimedChannel* aChannel,
                                      nsIHttpChannel* aHttpChannel,
                                      DOMHighResTimeStamp aZeroTime)
   : mPerformance(aPerformance),
     mFetchStart(0.0),
-    mZeroTime(aZeroTime),
+    mZeroTime(TimerClamping::ReduceMsTimeValue(aZeroTime)),
     mRedirectCount(0),
     mTimingAllowed(true),
     mAllRedirectsSameOrigin(true),
     mInitialized(!!aChannel),
     mReportCrossOriginRedirect(true)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 
@@ -84,17 +84,17 @@ PerformanceTiming::FetchStartHighRes()
       return mZeroTime;
     }
     MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
         "valid if the performance timing is enabled");
     mFetchStart = (!mAsyncOpen.IsNull())
         ? TimeStampToDOMHighRes(mAsyncOpen)
         : 0.0;
   }
-  return mFetchStart;
+  return TimerClamping::ReduceMsTimeValue(mFetchStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::FetchStart()
 {
   return static_cast<int64_t>(FetchStartHighRes());
 }
 
@@ -170,17 +170,17 @@ PerformanceTiming::ShouldReportCrossOrig
  * @return a valid timing if the Performance Timing is enabled
  */
 DOMHighResTimeStamp
 PerformanceTiming::RedirectStartHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
-  return TimeStampToDOMHighResOrFetchStart(mRedirectStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(mRedirectStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RedirectStart()
 {
   if (!IsInitialized()) {
     return 0;
   }
@@ -203,17 +203,17 @@ PerformanceTiming::RedirectStart()
  * @return a valid timing if the Performance Timing is enabled
  */
 DOMHighResTimeStamp
 PerformanceTiming::RedirectEndHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
-  return TimeStampToDOMHighResOrFetchStart(mRedirectEnd);
+  return TimeStampToReducedDOMHighResOrFetchStart(mRedirectEnd);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RedirectEnd()
 {
   if (!IsInitialized()) {
     return 0;
   }
@@ -226,82 +226,82 @@ PerformanceTiming::RedirectEnd()
 }
 
 DOMHighResTimeStamp
 PerformanceTiming::DomainLookupStartHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
-  return TimeStampToDOMHighResOrFetchStart(mDomainLookupStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(mDomainLookupStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::DomainLookupStart()
 {
   return static_cast<int64_t>(DomainLookupStartHighRes());
 }
 
 DOMHighResTimeStamp
 PerformanceTiming::DomainLookupEndHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
   return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes()
-                                   : TimeStampToDOMHighRes(mDomainLookupEnd);
+                                   : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mDomainLookupEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::DomainLookupEnd()
 {
   return static_cast<int64_t>(DomainLookupEndHighRes());
 }
 
 DOMHighResTimeStamp
 PerformanceTiming::ConnectStartHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
   return mConnectStart.IsNull() ? DomainLookupEndHighRes()
-                                : TimeStampToDOMHighRes(mConnectStart);
+                                : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mConnectStart));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectStart()
 {
   return static_cast<int64_t>(ConnectStartHighRes());
 }
 
 DOMHighResTimeStamp
 PerformanceTiming::ConnectEndHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
   return mConnectEnd.IsNull() ? ConnectStartHighRes()
-                              : TimeStampToDOMHighRes(mConnectEnd);
+                              : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mConnectEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectEnd()
 {
   return static_cast<int64_t>(ConnectEndHighRes());
 }
 
 DOMHighResTimeStamp
 PerformanceTiming::RequestStartHighRes()
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
-  return TimeStampToDOMHighResOrFetchStart(mRequestStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(mRequestStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RequestStart()
 {
   return static_cast<int64_t>(RequestStartHighRes());
 }
 
@@ -310,17 +310,17 @@ PerformanceTiming::ResponseStartHighRes(
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
     return mZeroTime;
   }
   if (mResponseStart.IsNull() ||
      (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) {
     mResponseStart = mCacheReadStart;
   }
-  return TimeStampToDOMHighResOrFetchStart(mResponseStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(mResponseStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ResponseStart()
 {
   return static_cast<int64_t>(ResponseStartHighRes());
 }
 
@@ -331,17 +331,17 @@ PerformanceTiming::ResponseEndHighRes()
     return mZeroTime;
   }
   if (mResponseEnd.IsNull() ||
      (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
     mResponseEnd = mCacheReadEnd;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
   return mResponseEnd.IsNull() ? ResponseStartHighRes()
-                               : TimeStampToDOMHighRes(mResponseEnd);
+                               : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mResponseEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ResponseEnd()
 {
   return static_cast<int64_t>(ResponseEndHighRes());
 }
 
old mode 100644
new mode 100755
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PerformanceTiming_h
 #define mozilla_dom_PerformanceTiming_h
 
 #include "mozilla/Attributes.h"
 #include "nsContentUtils.h"
 #include "nsDOMNavigationTiming.h"
+#include "mozilla/TimerClamping.h"
 #include "nsWrapperCache.h"
 #include "Performance.h"
 
 class nsIHttpChannel;
 class nsITimedChannel;
 
 namespace mozilla {
 namespace dom {
@@ -63,20 +64,20 @@ public:
    * @param   aStamp
    *          The TimeStamp recorded for a specific event. This TimeStamp can
    *          be null.
    * @return  the duration of an event with a given TimeStamp, relative to the
    *          navigationStart TimeStamp (the moment the user landed on the
    *          page), if the given TimeStamp is valid. Otherwise, it will return
    *          the FetchStart timing value.
    */
-  inline DOMHighResTimeStamp TimeStampToDOMHighResOrFetchStart(TimeStamp aStamp)
+  inline DOMHighResTimeStamp TimeStampToReducedDOMHighResOrFetchStart(TimeStamp aStamp)
   {
     return (!aStamp.IsNull())
-        ? TimeStampToDOMHighRes(aStamp)
+        ? TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(aStamp))
         : FetchStartHighRes();
   }
 
   /**
    * The nsITimedChannel records an absolute timestamp for each event.
    * The nsDOMNavigationTiming will record the moment when the user landed on
    * the page. This is a window.performance unique timestamp, so it can be used
    * for all the events (navigation timing and resource timing events).
@@ -114,33 +115,33 @@ public:
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // PerformanceNavigation WebIDL methods
   DOMTimeMilliSec NavigationStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetNavigationStart();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetNavigationStart());
   }
 
   DOMTimeMilliSec UnloadEventStart()
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetUnloadEventStart();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventStart());
   }
 
   DOMTimeMilliSec UnloadEventEnd()
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetUnloadEventEnd();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd());
   }
 
   uint16_t GetRedirectCount() const;
 
   // Checks if the resource is either same origin as the page that started
   // the load, or if the response contains the Timing-Allow-Origin header
   // with a value of * or matching the domain of the loading Principal
   bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel);
@@ -178,65 +179,65 @@ public:
   DOMTimeMilliSec ResponseStart();
   DOMTimeMilliSec ResponseEnd();
 
   DOMTimeMilliSec DomLoading()
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetDomLoading();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetDomLoading());
   }
 
   DOMTimeMilliSec DomInteractive() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetDomInteractive();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetDomInteractive());
   }
 
   DOMTimeMilliSec DomContentLoadedEventStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetDomContentLoadedEventStart();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetDomContentLoadedEventStart());
   }
 
   DOMTimeMilliSec DomContentLoadedEventEnd() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetDomContentLoadedEventEnd();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetDomContentLoadedEventEnd());
   }
 
   DOMTimeMilliSec DomComplete() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetDomComplete();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetDomComplete());
   }
 
   DOMTimeMilliSec LoadEventStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetLoadEventStart();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetLoadEventStart());
   }
 
   DOMTimeMilliSec LoadEventEnd() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled()) {
       return 0;
     }
-    return GetDOMTiming()->GetLoadEventEnd();
+    return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetLoadEventEnd());
   }
 
 private:
   ~PerformanceTiming();
 
   bool IsInitialized() const;
   void InitializeTimingInfo(nsITimedChannel* aChannel);
 
old mode 100644
new mode 100755
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1227,17 +1227,20 @@ date_parse(JSContext* cx, unsigned argc,
 
     args.rval().set(TimeValue(result));
     return true;
 }
 
 static ClippedTime
 NowAsMillis()
 {
-    return TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC);
+    const double maxResolutionMs = 2;
+    double timestamp = static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC;
+    timestamp = floor(timestamp / maxResolutionMs) * maxResolutionMs;
+    return TimeClip(timestamp);
 }
 
 bool
 js::date_now(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(TimeValue(NowAsMillis()));
     return true;