Bug 1440195 Add a random context seed to the Performance APIs r=baku
authorTom Ritter <tom@mozilla.com>
Tue, 13 Mar 2018 12:36:34 -0500
changeset 408366 b67bb0e7eddeff07208f9fd8449a7de983d17615
parent 408365 90dd04c11480e5220bd300a82676c4077e606770
child 408367 d79b4a990405e0ab7cd6d1b3c2688ddaf0c71470
push id33637
push usertoros@mozilla.com
push dateFri, 16 Mar 2018 09:53:55 +0000
treeherdermozilla-central@d8e8ec54ed9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1440195
milestone61.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 1440195 Add a random context seed to the Performance APIs r=baku We attach it to WorkerPrivate and DOMNavigationTiming so it will be re-used when it should. WorkerPrivate is used in the Performance APIs, Performance Storage Worker, and Event. DOMNavigationTiming is used only in the Performance APIs, but the crucial part is that when the individual DOMNavigationTiming object is re-used, so will the context seed. This in particular came up with the nav2_test_document_open.html Web Platform Test which illustrated the fact that even if you .open() a new document, the performance navigation data is not supposed to change. MozReview-Commit-ID: GIv6biEo2jY
dom/base/nsDOMNavigationTiming.h
dom/events/Event.cpp
dom/performance/Performance.cpp
dom/performance/Performance.h
dom/performance/PerformanceMainThread.cpp
dom/performance/PerformanceMainThread.h
dom/performance/PerformanceNavigationTiming.h
dom/performance/PerformanceStorageWorker.cpp
dom/performance/PerformanceTiming.cpp
dom/performance/PerformanceTiming.h
dom/performance/PerformanceWorker.cpp
dom/performance/PerformanceWorker.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/base/nsDOMNavigationTiming.h
+++ b/dom/base/nsDOMNavigationTiming.h
@@ -5,25 +5,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDOMNavigationTiming_h___
 #define nsDOMNavigationTiming_h___
 
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "mozilla/WeakPtr.h"
+#include "mozilla/RelativeTimeline.h"
 #include "mozilla/TimeStamp.h"
 
 class nsDocShell;
 class nsIURI;
 
 typedef unsigned long long DOMTimeMilliSec;
 typedef double DOMHighResTimeStamp;
 
 class nsDOMNavigationTiming final
+  : public mozilla::RelativeTimeline
 {
 public:
   enum Type {
     TYPE_NAVIGATE = 0,
     TYPE_RELOAD = 1,
     TYPE_BACK_FORWARD = 2,
     TYPE_RESERVED = 255,
   };
old mode 100755
new mode 100644
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -1096,18 +1096,19 @@ Event::DefaultPrevented(CallerType aCall
 
 double
 Event::TimeStamp()
 {
   if (!sReturnHighResTimeStamp) {
     // In the situation where you have set a very old, not-very-supported
     // non-default preference, we will always reduce the precision,
     // regardless of system principal or not.
+    // The timestamp is absolute, so we supply a zero context mix-in.
     double ret = static_cast<double>(mEvent->mTime);
-    return nsRFPService::ReduceTimePrecisionAsMSecs(ret);
+    return nsRFPService::ReduceTimePrecisionAsMSecs(ret, 0);
   }
 
   if (mEvent->mTimeStamp.IsNull()) {
     return 0.0;
   }
 
   if (mIsMainThreadEvent) {
     if (NS_WARN_IF(!mOwner)) {
@@ -1124,27 +1125,29 @@ Event::TimeStamp()
       return 0.0;
     }
 
     double ret = perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp);
     MOZ_ASSERT(mOwner->PrincipalOrNull());
     if (nsContentUtils::IsSystemPrincipal(mOwner->PrincipalOrNull()))
       return ret;
 
-    return nsRFPService::ReduceTimePrecisionAsMSecs(ret);
+    return nsRFPService::ReduceTimePrecisionAsMSecs(ret,
+      perf->GetRandomTimelineSeed());
   }
 
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
   double ret = workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp);
   if (workerPrivate->UsesSystemPrincipal())
     return ret;
 
-  return nsRFPService::ReduceTimePrecisionAsMSecs(ret);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(ret,
+    workerPrivate->GetRandomTimelineSeed());
 }
 
 NS_IMETHODIMP
 Event::GetDefaultPrevented(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
   // This method must be called by only event handlers implemented by C++.
   // Then, the handlers must handle default action.  So, this method don't need
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -95,17 +95,17 @@ Performance::Now()
 {
   DOMHighResTimeStamp rawTime = NowUnclamped();
   if (mSystemPrincipal) {
     return rawTime;
   }
 
   const double maxResolutionMs = 0.020;
   DOMHighResTimeStamp minimallyClamped = floor(rawTime / maxResolutionMs) * maxResolutionMs;
-  return nsRFPService::ReduceTimePrecisionAsMSecs(minimallyClamped);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(minimallyClamped, GetRandomTimelineSeed());
 }
 
 DOMHighResTimeStamp
 Performance::NowUnclamped() const
 {
   TimeDuration duration = TimeStamp::Now() - CreationTimeStamp();
   return duration.ToMilliseconds();
 }
@@ -118,17 +118,18 @@ Performance::TimeOrigin()
   }
 
   MOZ_ASSERT(mPerformanceService);
   DOMHighResTimeStamp rawTimeOrigin = mPerformanceService->TimeOrigin(CreationTimeStamp());
   if (mSystemPrincipal) {
     return rawTimeOrigin;
   }
 
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimeOrigin);
+  // Time Origin is an absolute timestamp, so we supply a 0 context mix-in
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimeOrigin, 0);
 }
 
 JSObject*
 Performance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -103,16 +103,18 @@ public:
 
   virtual TimeStamp CreationTimeStamp() const = 0;
 
   uint64_t IsSystemPrincipal()
   {
     return mSystemPrincipal;
   }
 
+  virtual uint64_t GetRandomTimelineSeed() = 0;
+
   void MemoryPressure();
 
   size_t SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   void InsertResourceEntry(PerformanceEntry* aEntry);
 
 protected:
old mode 100755
new mode 100644
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -280,16 +280,17 @@ PerformanceMainThread::CreationTime() co
 {
   return GetDOMTiming()->GetNavigationStart();
 }
 
 void
 PerformanceMainThread::EnsureDocEntry()
 {
   if (!mDocEntry && nsContentUtils::IsPerformanceNavigationTimingEnabled()) {
+
     UniquePtr<PerformanceTimingData> timing(
       new PerformanceTimingData(mChannel, nullptr, 0));
 
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
     if (httpChannel) {
       timing->SetPropertiesFromHttpChannel(httpChannel);
     }
 
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -44,16 +44,21 @@ public:
   virtual void GetMozMemory(JSContext *aCx,
                             JS::MutableHandle<JSObject*> aObj) override;
 
   virtual nsDOMNavigationTiming* GetDOMTiming() const override
   {
     return mDOMTiming;
   }
 
+  virtual uint64_t GetRandomTimelineSeed() override
+  {
+    return GetDOMTiming()->GetRandomTimelineSeed();
+  }
+
   virtual nsITimedChannel* GetChannel() const override
   {
     return mChannel;
   }
 
   // The GetEntries* methods need to be overriden in order to add the
   // the document entry of type navigation.
   virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
old mode 100755
new mode 100644
--- a/dom/performance/PerformanceNavigationTiming.h
+++ b/dom/performance/PerformanceNavigationTiming.h
@@ -38,17 +38,18 @@ public:
     }
 
   DOMHighResTimeStamp Duration() const override
   {
     DOMHighResTimeStamp rawDuration = LoadEventEnd() - StartTime();
     if (mPerformance->IsSystemPrincipal()) {
       return rawDuration;
     }
-    return nsRFPService::ReduceTimePrecisionAsMSecs(rawDuration);
+    return nsRFPService::ReduceTimePrecisionAsMSecs(rawDuration,
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMHighResTimeStamp StartTime() const override
   {
     return 0;
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
old mode 100755
new mode 100644
old mode 100755
new mode 100644
--- a/dom/performance/PerformanceTiming.cpp
+++ b/dom/performance/PerformanceTiming.cpp
@@ -78,17 +78,18 @@ PerformanceTiming::PerformanceTiming(Per
                                      DOMHighResTimeStamp aZeroTime)
   : mPerformance(aPerformance)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 
   mTimingData.reset(new PerformanceTimingData(aChannel, aHttpChannel,
     aPerformance->IsSystemPrincipal()
     ? aZeroTime
-    : nsRFPService::ReduceTimePrecisionAsMSecs(aZeroTime)));
+    : nsRFPService::ReduceTimePrecisionAsMSecs(aZeroTime,
+        aPerformance->GetRandomTimelineSeed())));
 
   // Non-null aHttpChannel implies that this PerformanceTiming object is being
   // used for subresources, which is irrelevant to this probe.
   if (!aHttpChannel &&
       nsContentUtils::IsPerformanceTimingEnabled() &&
       IsTopLevelContentDocument()) {
     Telemetry::Accumulate(Telemetry::TIME_TO_RESPONSE_START_MS,
                           mTimingData->ResponseStartHighRes(aPerformance) -
@@ -252,17 +253,18 @@ PerformanceTimingData::FetchStartHighRes
       } else {
         mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
       }
     }
   }
   if (aPerformance->IsSystemPrincipal()) {
     return mFetchStart;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(mFetchStart);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(mFetchStart,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::FetchStart()
 {
   return static_cast<int64_t>(mTimingData->FetchStartHighRes(mPerformance));
 }
 
@@ -331,33 +333,35 @@ PerformanceTimingData::AsyncOpenHighRes(
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) {
     return mZeroTime;
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMHighResTimeStamp
 PerformanceTimingData::WorkerStartHighRes(Performance* aPerformance)
 {
   MOZ_ASSERT(aPerformance);
 
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) {
     return mZeroTime;
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mWorkerStart);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 /**
  * RedirectStartHighRes() is used by both the navigation timing and the
  * resource timing. Since, navigation timing and resource timing check and
  * interpret cross-domain redirects in a different manner,
  * RedirectStartHighRes() will make no checks for cross-domain redirect.
  * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
@@ -460,17 +464,18 @@ PerformanceTimingData::DomainLookupEndHi
   // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
   if (mDomainLookupEnd.IsNull()) {
     return DomainLookupStartHighRes(aPerformance);
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::DomainLookupEnd()
 {
   return static_cast<int64_t>(mTimingData->DomainLookupEndHighRes(mPerformance));
 }
 
@@ -485,17 +490,18 @@ PerformanceTimingData::ConnectStartHighR
   }
   if (mConnectStart.IsNull()) {
     return DomainLookupEndHighRes(aPerformance);
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mConnectStart);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectStart()
 {
   return static_cast<int64_t>(mTimingData->ConnectStartHighRes(mPerformance));
 }
 
@@ -514,17 +520,18 @@ PerformanceTimingData::SecureConnectionS
   }
   if (mSecureConnectionStart.IsNull()) {
     return mZeroTime;
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::SecureConnectionStart()
 {
   return static_cast<int64_t>(mTimingData->SecureConnectionStartHighRes(mPerformance));
 }
 
@@ -540,17 +547,18 @@ PerformanceTimingData::ConnectEndHighRes
   // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
   if (mConnectEnd.IsNull()) {
     return ConnectStartHighRes(aPerformance);
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mConnectEnd);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectEnd()
 {
   return static_cast<int64_t>(mTimingData->ConnectEndHighRes(mPerformance));
 }
 
@@ -623,17 +631,18 @@ PerformanceTimingData::ResponseEndHighRe
   // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
   if (mResponseEnd.IsNull()) {
     return ResponseStartHighRes(aPerformance);
   }
   DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mResponseEnd);
   if (aPerformance->IsSystemPrincipal()) {
     return rawValue;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue);
+  return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue,
+    aPerformance->GetRandomTimelineSeed());
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ResponseEnd()
 {
   return static_cast<int64_t>(mTimingData->ResponseEndHighRes(mPerformance));
 }
 
old mode 100755
new mode 100644
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -86,17 +86,18 @@ public:
       return FetchStartHighRes(aPerformance);
     }
 
     DOMHighResTimeStamp rawTimestamp = TimeStampToDOMHighRes(aPerformance, aStamp);
     if (aPerformance->IsSystemPrincipal()) {
       return rawTimestamp;
     }
 
-    return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimestamp);
+    return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimestamp,
+      aPerformance->GetRandomTimelineSeed());
   }
 
   /**
    * 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).
    *
@@ -273,43 +274,46 @@ public:
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetNavigationStart();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetNavigationStart());
+      GetDOMTiming()->GetNavigationStart(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec UnloadEventStart()
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetUnloadEventStart();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetUnloadEventStart());
+      GetDOMTiming()->GetUnloadEventStart(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec UnloadEventEnd()
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetUnloadEventEnd();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetUnloadEventEnd());
+      GetDOMTiming()->GetUnloadEventEnd(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   // Low resolution (used by navigation timing)
   DOMTimeMilliSec FetchStart();
   DOMTimeMilliSec RedirectStart();
   DOMTimeMilliSec RedirectEnd();
   DOMTimeMilliSec DomainLookupStart();
   DOMTimeMilliSec DomainLookupEnd();
@@ -325,108 +329,116 @@ public:
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetDomLoading();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetDomLoading());
+      GetDOMTiming()->GetDomLoading(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec DomInteractive() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetDomInteractive();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetDomInteractive());
+      GetDOMTiming()->GetDomInteractive(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec DomContentLoadedEventStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetDomContentLoadedEventStart();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetDomContentLoadedEventStart());
+      GetDOMTiming()->GetDomContentLoadedEventStart(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec DomContentLoadedEventEnd() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetDomContentLoadedEventEnd();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetDomContentLoadedEventEnd());
+      GetDOMTiming()->GetDomContentLoadedEventEnd(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec DomComplete() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetDomComplete();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetDomComplete());
+      GetDOMTiming()->GetDomComplete(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec LoadEventStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetLoadEventStart();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetLoadEventStart());
+      GetDOMTiming()->GetLoadEventStart(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec LoadEventEnd() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetLoadEventEnd();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetLoadEventEnd());
+      GetDOMTiming()->GetLoadEventEnd(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   DOMTimeMilliSec TimeToNonBlankPaint() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     if (mPerformance->IsSystemPrincipal()) {
       return GetDOMTiming()->GetTimeToNonBlankPaint();
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
-      GetDOMTiming()->GetTimeToNonBlankPaint());
+      GetDOMTiming()->GetTimeToNonBlankPaint(),
+      mPerformance->GetRandomTimelineSeed());
   }
 
   PerformanceTimingData* Data() const
   {
     return mTimingData.get();
   }
 
 private:
--- a/dom/performance/PerformanceWorker.cpp
+++ b/dom/performance/PerformanceWorker.cpp
@@ -44,10 +44,16 @@ PerformanceWorker::CreationTimeStamp() c
 }
 
 DOMHighResTimeStamp
 PerformanceWorker::CreationTime() const
 {
   return mWorkerPrivate->CreationTime();
 }
 
+uint64_t
+PerformanceWorker::GetRandomTimelineSeed()
+{
+  return mWorkerPrivate->GetRandomTimelineSeed();
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/performance/PerformanceWorker.h
+++ b/dom/performance/PerformanceWorker.h
@@ -48,16 +48,18 @@ public:
   }
 
   virtual nsDOMNavigationTiming* GetDOMTiming() const override
   {
     MOZ_CRASH("This should not be called on workers.");
     return nullptr;
   }
 
+  virtual uint64_t GetRandomTimelineSeed() override;
+
   virtual nsITimedChannel* GetChannel() const override
   {
     MOZ_CRASH("This should not be called on workers.");
     return nullptr;
   }
 
 protected:
   ~PerformanceWorker();
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -30,22 +30,24 @@
 #include "mozilla/ThreadEventQueue.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/WorkerTimelineMarker.h"
 #include "nsCycleCollector.h"
 #include "nsNetUtil.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPermissionManager.h"
+#include "nsIRandomGenerator.h"
 #include "nsIScriptError.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsPrintfCString.h"
 #include "nsQueryObject.h"
+#include "nsRFPService.h"
 #include "nsSandboxFlags.h"
 #include "nsUTF8Utils.h"
 
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "mozilla/dom/ServiceWorkerEvents.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "SharedWorker.h"
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_workerprivate_h__
 #define mozilla_dom_workers_workerprivate_h__
 
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/RelativeTimeline.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIEventTarget.h"
 #include "nsTObserverArray.h"
 
 #include "mozilla/dom/Worker.h"
 #include "mozilla/dom/WorkerHolder.h"
 #include "mozilla/dom/WorkerLoadInfo.h"
 #include "mozilla/dom/workerinternals/JSSettings.h"
@@ -97,16 +98,17 @@ public:
   void
   AssertCurrentThreadOwns() const
   {
     mMutex->AssertCurrentThreadOwns();
   }
 };
 
 class WorkerPrivate
+  : public RelativeTimeline
 {
 public:
   struct LocationInfo
   {
     nsCString mHref;
     nsCString mProtocol;
     nsCString mHost;
     nsCString mHostname;