Bug 1425458 - Resource timing entries Workers - part 2 - PerformanceTimingData, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 24 Jan 2018 17:17:31 +0100
changeset 400753 cd25cfc3defe1fe948e0d6b408d841bce491a631
parent 400752 8101a66c3180397c79a8e9bb18f5ade2b19cf976
child 400754 619d2fe88e78799e1e5606159e228597c07f8021
push id99218
push useramarchesini@mozilla.com
push dateThu, 25 Jan 2018 11:12:22 +0000
treeherdermozilla-inbound@4f292fa4e4a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1425458
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 1425458 - Resource timing entries Workers - part 2 - PerformanceTimingData, r=smaug
dom/performance/PerformanceMainThread.cpp
dom/performance/PerformanceNavigation.cpp
dom/performance/PerformanceNavigationTiming.cpp
dom/performance/PerformanceNavigationTiming.h
dom/performance/PerformanceResourceTiming.cpp
dom/performance/PerformanceResourceTiming.h
dom/performance/PerformanceTiming.cpp
dom/performance/PerformanceTiming.h
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -154,24 +154,24 @@ PerformanceMainThread::AddEntry(nsIHttpC
     }
 
     // The nsITimedChannel argument will be used to gather all the timings.
     // The nsIHttpChannel argument will be used to check if any cross-origin
     // redirects occurred.
     // The last argument is the "zero time" (offset). Since we don't want
     // any offset for the resource timing, this will be set to "0" - the
     // resource timing returns a relative timing (no offset).
-    RefPtr<PerformanceTiming> performanceTiming =
-        new PerformanceTiming(this, timedChannel, channel,
-            0);
+    UniquePtr<PerformanceTimingData> performanceTimingData(
+        new PerformanceTimingData(timedChannel, channel, 0));
 
     // The PerformanceResourceTiming object will use the PerformanceTiming
     // object to get all the required timings.
     RefPtr<PerformanceResourceTiming> performanceEntry =
-      new PerformanceResourceTiming(performanceTiming, this, entryName, channel);
+      new PerformanceResourceTiming(Move(performanceTimingData), this,
+                                    entryName);
 
     // If the initiator type had no valid value, then set it to the default
     // ("other") value.
     if (initiatorType.IsEmpty()) {
       initiatorType = NS_LITERAL_STRING("other");
     }
     performanceEntry->SetInitiatorType(initiatorType);
     InsertResourceEntry(performanceEntry);
@@ -320,21 +320,25 @@ 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);
-    RefPtr<PerformanceTiming> timing =
-      new PerformanceTiming(this, mChannel, nullptr, 0);
-    mDocEntry = new PerformanceNavigationTiming(timing, this,
-                                                httpChannel);
+    if (httpChannel) {
+      timing->SetPropertiesFromHttpChannel(httpChannel);
+    }
+
+    mDocEntry = new PerformanceNavigationTiming(Move(timing), this);
   }
 }
 
 
 void
 PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
 {
   // We return an empty list when 'privacy.resistFingerprinting' is on.
--- a/dom/performance/PerformanceNavigation.cpp
+++ b/dom/performance/PerformanceNavigation.cpp
@@ -31,13 +31,13 @@ PerformanceNavigation::WrapObject(JSCont
                                   JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceNavigationBinding::Wrap(cx, this, aGivenProto);
 }
 
 uint16_t
 PerformanceNavigation::RedirectCount() const
 {
-  return GetPerformanceTiming()->GetRedirectCount();
+  return GetPerformanceTiming()->Data()->GetRedirectCount();
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/performance/PerformanceNavigationTiming.cpp
+++ b/dom/performance/PerformanceNavigationTiming.cpp
@@ -19,65 +19,65 @@ JSObject*
 PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceNavigationTimingBinding::Wrap(aCx, this, aGivenProto);
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::UnloadEventStart() const
 {
-  return mTiming->GetDOMTiming()->GetUnloadEventStartHighRes();
+  return mPerformance->GetDOMTiming()->GetUnloadEventStartHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::UnloadEventEnd() const
 {
-  return mTiming->GetDOMTiming()->GetUnloadEventEndHighRes();
+  return mPerformance->GetDOMTiming()->GetUnloadEventEndHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::DomInteractive() const
 {
-  return mTiming->GetDOMTiming()->GetDomInteractiveHighRes();
+  return mPerformance->GetDOMTiming()->GetDomInteractiveHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::DomContentLoadedEventStart() const
 {
-  return mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes();
+  return mPerformance->GetDOMTiming()->GetDomContentLoadedEventStartHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::DomContentLoadedEventEnd() const
 {
-  return mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes();
+  return mPerformance->GetDOMTiming()->GetDomContentLoadedEventEndHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::DomComplete() const
 {
-  return mTiming->GetDOMTiming()->GetDomCompleteHighRes();
+  return mPerformance->GetDOMTiming()->GetDomCompleteHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::LoadEventStart() const
 {
-  return mTiming->GetDOMTiming()->GetLoadEventStartHighRes();
+  return mPerformance->GetDOMTiming()->GetLoadEventStartHighRes();
 }
 
 DOMHighResTimeStamp
 PerformanceNavigationTiming::LoadEventEnd() const
 {
-  return mTiming->GetDOMTiming()->GetLoadEventEndHighRes();
+  return mPerformance->GetDOMTiming()->GetLoadEventEndHighRes();
 }
 
 NavigationType
 PerformanceNavigationTiming::Type() const
 {
-  switch(mTiming->GetDOMTiming()->GetType()) {
+  switch(mPerformance->GetDOMTiming()->GetType()) {
     case nsDOMNavigationTiming::TYPE_NAVIGATE:
       return NavigationType::Navigate;
       break;
     case nsDOMNavigationTiming::TYPE_RELOAD:
       return NavigationType::Reload;
       break;
     case nsDOMNavigationTiming::TYPE_BACK_FORWARD:
       return NavigationType::Back_forward;
@@ -87,10 +87,10 @@ PerformanceNavigationTiming::Type() cons
       // We fallback to the default of Navigate.
       return NavigationType::Navigate;
   }
 }
 
 uint16_t
 PerformanceNavigationTiming::RedirectCount() const
 {
-  return mTiming->GetRedirectCount();
+  return mTimingData->GetRedirectCount();
 }
--- a/dom/performance/PerformanceNavigationTiming.h
+++ b/dom/performance/PerformanceNavigationTiming.h
@@ -24,21 +24,20 @@ class PerformanceNavigationTiming final
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // Note that aPerformanceTiming must be initalized with zeroTime = 0
   // so that timestamps are relative to startTime, as opposed to the
   // performance.timing object for which timestamps are absolute and has a
   // zeroTime initialized to navigationStart
-  explicit PerformanceNavigationTiming(PerformanceTiming* aPerformanceTiming,
-                                       Performance* aPerformance,
-                                       nsIHttpChannel* aChannel)
-    : PerformanceResourceTiming(aPerformanceTiming, aPerformance,
-                                NS_LITERAL_STRING("document"), aChannel) {
+  PerformanceNavigationTiming(UniquePtr<PerformanceTimingData>&& aPerformanceTiming,
+                              Performance* aPerformance)
+    : PerformanceResourceTiming(Move(aPerformanceTiming), aPerformance,
+                                NS_LITERAL_STRING("document")) {
       SetEntryType(NS_LITERAL_STRING("navigation"));
       SetInitiatorType(NS_LITERAL_STRING("navigation"));
     }
 
   DOMHighResTimeStamp Duration() const override
   {
     return nsRFPService::ReduceTimePrecisionAsMSecs(LoadEventEnd() - StartTime());
   }
--- a/dom/performance/PerformanceResourceTiming.cpp
+++ b/dom/performance/PerformanceResourceTiming.cpp
@@ -6,90 +6,60 @@
 
 #include "PerformanceResourceTiming.h"
 #include "mozilla/dom/PerformanceResourceTimingBinding.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming,
                                    PerformanceEntry,
-                                   mTiming)
+                                   mPerformance)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceResourceTiming,
                                                PerformanceEntry)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceResourceTiming)
 NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
 
 NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 
-PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
+PerformanceResourceTiming::PerformanceResourceTiming(UniquePtr<PerformanceTimingData>&& aPerformanceTiming,
                                                      Performance* aPerformance,
-                                                     const nsAString& aName,
-                                                     nsIHttpChannel* aChannel)
-: PerformanceEntry(aPerformance->GetParentObject(), aName, NS_LITERAL_STRING("resource")),
-  mTiming(aPerformanceTiming),
-  mEncodedBodySize(0),
-  mTransferSize(0),
-  mDecodedBodySize(0)
+                                                     const nsAString& aName)
+  : PerformanceEntry(aPerformance->GetParentObject(), aName, NS_LITERAL_STRING("resource"))
+  , mTimingData(Move(aPerformanceTiming))
+  , mPerformance(aPerformance)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
-  SetPropertiesFromChannel(aChannel);
-}
-
-void
-PerformanceResourceTiming::SetPropertiesFromChannel(nsIHttpChannel* aChannel)
-{
-  if (!aChannel) {
-    return;
-  }
-
-  nsAutoCString protocol;
-  Unused << aChannel->GetProtocolVersion(protocol);
-  SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol));
-
-  uint64_t encodedBodySize = 0;
-  Unused << aChannel->GetEncodedBodySize(&encodedBodySize);
-  SetEncodedBodySize(encodedBodySize);
-
-  uint64_t transferSize = 0;
-  Unused << aChannel->GetTransferSize(&transferSize);
-  SetTransferSize(transferSize);
-
-  uint64_t decodedBodySize = 0;
-  Unused << aChannel->GetDecodedBodySize(&decodedBodySize);
-  if (decodedBodySize == 0) {
-    decodedBodySize = encodedBodySize;
-  }
-  SetDecodedBodySize(decodedBodySize);
 }
 
 PerformanceResourceTiming::~PerformanceResourceTiming()
 {
 }
 
 DOMHighResTimeStamp
 PerformanceResourceTiming::StartTime() const
 {
   // Force the start time to be the earliest of:
   //  - RedirectStart
   //  - WorkerStart
   //  - AsyncOpen
   // Ignore zero values.  The RedirectStart and WorkerStart values
   // can come from earlier redirected channels prior to the AsyncOpen
   // time being recorded.
-  DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes();
+  DOMHighResTimeStamp redirect =
+    mTimingData->RedirectStartHighRes(mPerformance);
   redirect = redirect ? redirect : DBL_MAX;
 
-  DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes();
+  DOMHighResTimeStamp worker = mTimingData->WorkerStartHighRes(mPerformance);
   worker = worker ? worker : DBL_MAX;
 
-  DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes();
+  DOMHighResTimeStamp asyncOpen = mTimingData->AsyncOpenHighRes(mPerformance);
 
   return std::min(asyncOpen, std::min(redirect, worker));
 }
 
 JSObject*
 PerformanceResourceTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceResourceTimingBinding::Wrap(aCx, this, aGivenProto);
@@ -101,10 +71,12 @@ PerformanceResourceTiming::SizeOfIncludi
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
 size_t
 PerformanceResourceTiming::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return PerformanceEntry::SizeOfExcludingThis(aMallocSizeOf) +
          mInitiatorType.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
-         mNextHopProtocol.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+         (mTimingData
+            ? mTimingData->NextHopProtocol().SizeOfExcludingThisIfUnshared(aMallocSizeOf)
+            : 0);
 }
--- a/dom/performance/PerformanceResourceTiming.h
+++ b/dom/performance/PerformanceResourceTiming.h
@@ -2,19 +2,18 @@
 /* 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 mozilla_dom_PerformanceResourceTiming_h___
 #define mozilla_dom_PerformanceResourceTiming_h___
 
+#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
-#include "nsIChannel.h"
-#include "nsITimedChannel.h"
 #include "Performance.h"
 #include "PerformanceEntry.h"
 #include "PerformanceTiming.h"
 
 namespace mozilla {
 namespace dom {
 
 // http://www.w3.org/TR/resource-timing/#performanceresourcetiming
@@ -23,20 +22,19 @@ class PerformanceResourceTiming : public
 public:
   typedef mozilla::TimeStamp TimeStamp;
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
       PerformanceResourceTiming,
       PerformanceEntry)
 
-  PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
+  PerformanceResourceTiming(UniquePtr<PerformanceTimingData>&& aPerformanceTimingData,
                             Performance* aPerformance,
-                            const nsAString& aName,
-                            nsIHttpChannel* aChannel = nullptr);
+                            const nsAString& aName);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 
   virtual DOMHighResTimeStamp StartTime() const override;
 
   virtual DOMHighResTimeStamp Duration() const override
   {
@@ -50,150 +48,128 @@ public:
 
   void SetInitiatorType(const nsAString& aInitiatorType)
   {
     mInitiatorType = aInitiatorType;
   }
 
   void GetNextHopProtocol(nsAString& aNextHopProtocol) const
   {
-    aNextHopProtocol = mNextHopProtocol;
-  }
-
-  void SetNextHopProtocol(const nsAString& aNextHopProtocol)
-  {
-    mNextHopProtocol = aNextHopProtocol;
+    if (mTimingData) {
+      aNextHopProtocol = mTimingData->NextHopProtocol();
+    }
   }
 
   DOMHighResTimeStamp WorkerStart() const {
-    return mTiming
-        ? mTiming->WorkerStartHighRes()
+    return mTimingData
+        ? mTimingData->WorkerStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp FetchStart() const {
-    return mTiming
-        ? mTiming->FetchStartHighRes()
+    return mTimingData
+        ? mTimingData->FetchStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp RedirectStart() const {
     // We have to check if all the redirect URIs had the same origin (since
     // there is no check in RedirectEndHighRes())
-    return mTiming && mTiming->ShouldReportCrossOriginRedirect()
-        ? mTiming->RedirectStartHighRes()
+    return mTimingData && mTimingData->ShouldReportCrossOriginRedirect()
+        ? mTimingData->RedirectStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp RedirectEnd() const {
     // We have to check if all the redirect URIs had the same origin (since
     // there is no check in RedirectEndHighRes())
-    return mTiming && mTiming->ShouldReportCrossOriginRedirect()
-        ? mTiming->RedirectEndHighRes()
+    return mTimingData && mTimingData->ShouldReportCrossOriginRedirect()
+        ? mTimingData->RedirectEndHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp DomainLookupStart() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->DomainLookupStartHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->DomainLookupStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp DomainLookupEnd() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->DomainLookupEndHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->DomainLookupEndHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp ConnectStart() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->ConnectStartHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->ConnectStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp ConnectEnd() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->ConnectEndHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->ConnectEndHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp RequestStart() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->RequestStartHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->RequestStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp ResponseStart() const {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->ResponseStartHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ? mTimingData->ResponseStartHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp ResponseEnd() const {
-    return mTiming
-        ? mTiming->ResponseEndHighRes()
+    return mTimingData
+        ? mTimingData->ResponseEndHighRes(mPerformance)
         : 0;
   }
 
   DOMHighResTimeStamp SecureConnectionStart() const
   {
-    return mTiming && mTiming->TimingAllowed()
-        ? mTiming->SecureConnectionStartHighRes()
+    return mTimingData && mTimingData->TimingAllowed()
+        ?  mTimingData->SecureConnectionStartHighRes(mPerformance)
         : 0;
   }
 
   virtual const PerformanceResourceTiming* ToResourceTiming() const override
   {
     return this;
   }
 
   uint64_t TransferSize() const
   {
-    return mTiming && mTiming->TimingAllowed() ? mTransferSize : 0;
+    return mTimingData ? mTimingData->TransferSize() : 0;
   }
 
   uint64_t EncodedBodySize() const
   {
-    return mTiming && mTiming->TimingAllowed() ? mEncodedBodySize : 0;
+    return mTimingData ? mTimingData->EncodedBodySize() : 0;
   }
 
   uint64_t DecodedBodySize() const
   {
-    return mTiming && mTiming->TimingAllowed() ? mDecodedBodySize : 0;
-  }
-
-  void SetEncodedBodySize(uint64_t aEncodedBodySize)
-  {
-    mEncodedBodySize = aEncodedBodySize;
-  }
-
-  void SetTransferSize(uint64_t aTransferSize)
-  {
-    mTransferSize = aTransferSize;
-  }
-
-  void SetDecodedBodySize(uint64_t aDecodedBodySize)
-  {
-    mDecodedBodySize = aDecodedBodySize;
+    return mTimingData ? mTimingData->DecodedBodySize() : 0;
   }
 
   size_t
   SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
 protected:
   virtual ~PerformanceResourceTiming();
-  void SetPropertiesFromChannel(nsIHttpChannel* aChannel);
 
   size_t
   SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
   nsString mInitiatorType;
-  nsString mNextHopProtocol;
-  RefPtr<PerformanceTiming> mTiming;
-  uint64_t mEncodedBodySize;
-  uint64_t mTransferSize;
-  uint64_t mDecodedBodySize;
+  UniquePtr<PerformanceTimingData> mTimingData;
+  RefPtr<Performance> mPerformance;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_PerformanceResourceTiming_h___ */
--- a/dom/performance/PerformanceTiming.cpp
+++ b/dom/performance/PerformanceTiming.cpp
@@ -19,78 +19,76 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Pe
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)
 
 PerformanceTiming::PerformanceTiming(Performance* aPerformance,
                                      nsITimedChannel* aChannel,
                                      nsIHttpChannel* aHttpChannel,
                                      DOMHighResTimeStamp aZeroTime)
-  : mPerformance(aPerformance),
-    mFetchStart(0.0),
-    mZeroTime(nsRFPService::ReduceTimePrecisionAsMSecs(aZeroTime)),
-    mRedirectCount(0),
-    mTimingAllowed(true),
-    mAllRedirectsSameOrigin(true),
-    mInitialized(!!aChannel),
-    mReportCrossOriginRedirect(true)
+  : mPerformance(aPerformance)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 
+  mTimingData.reset(new PerformanceTimingData(aChannel, aHttpChannel,
+                                              aZeroTime));
+
+  // 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) -
+                            mTimingData->ZeroTime());
+  }
+}
+
+// Copy the timing info from the channel so we don't need to keep the channel
+// alive just to get the timestamps.
+PerformanceTimingData::PerformanceTimingData(nsITimedChannel* aChannel,
+                                             nsIHttpChannel* aHttpChannel,
+                                             DOMHighResTimeStamp aZeroTime)
+  : mZeroTime(0.0)
+  , mFetchStart(0.0)
+  , mEncodedBodySize(0)
+  , mTransferSize(0)
+  , mDecodedBodySize(0)
+  , mRedirectCount(0)
+  , mAllRedirectsSameOrigin(true)
+  , mReportCrossOriginRedirect(true)
+  , mSecureConnection(false)
+  , mTimingAllowed(true)
+  , mInitialized(false)
+{
+  mInitialized = !!aChannel;
+
+  mZeroTime = nsRFPService::ReduceTimePrecisionAsMSecs(aZeroTime);
   if (!nsContentUtils::IsPerformanceTimingEnabled() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     mZeroTime = 0;
   }
 
-  // The aHttpChannel argument is null if this PerformanceTiming object is
-  // being used for navigation timing (which is only relevant for documents).
-  // It has a non-null value if this PerformanceTiming object is being used
-  // for resource timing, which can include document loads, both toplevel and
-  // in subframes, and resources linked from a document.
-  if (aHttpChannel) {
-    mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel);
-    bool redirectsPassCheck = false;
-    aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
-    mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
-  }
-
-  mSecureConnection = false;
   nsCOMPtr<nsIURI> uri;
   if (aHttpChannel) {
     aHttpChannel->GetURI(getter_AddRefs(uri));
   } else {
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
     if (httpChannel) {
       httpChannel->GetURI(getter_AddRefs(uri));
     }
   }
 
   if (uri) {
     nsresult rv = uri->SchemeIs("https", &mSecureConnection);
     if (NS_FAILED(rv)) {
       mSecureConnection = false;
     }
   }
-  InitializeTimingInfo(aChannel);
 
-  // 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,
-                          ResponseStartHighRes() - mZeroTime);
-  }
-}
-
-// Copy the timing info from the channel so we don't need to keep the channel
-// alive just to get the timestamps.
-void
-PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
-{
   if (aChannel) {
     aChannel->GetAsyncOpen(&mAsyncOpen);
     aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
     aChannel->GetRedirectCount(&mRedirectCount);
     aChannel->GetRedirectStart(&mRedirectStart);
     aChannel->GetRedirectEnd(&mRedirectEnd);
     aChannel->GetDomainLookupStart(&mDomainLookupStart);
     aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
@@ -105,21 +103,21 @@ PerformanceTiming::InitializeTimingInfo(
 
     aChannel->GetDispatchFetchEventStart(&mWorkerStart);
     aChannel->GetHandleFetchEventStart(&mWorkerRequestStart);
     // TODO: Track when FetchEvent.respondWith() promise resolves as
     //       ServiceWorker interception responseStart?
     aChannel->GetHandleFetchEventEnd(&mWorkerResponseEnd);
 
     // The performance timing api essentially requires that the event timestamps
-    // have a strict relation with each other. The truth, however, is the browser
-    // engages in a number of speculative activities that sometimes mean connections
-    // and lookups begin at different times. Workaround that here by clamping
-    // these values to what we expect FetchStart to be.  This means the later of
-    // AsyncOpen or WorkerStart times.
+    // have a strict relation with each other. The truth, however, is the
+    // browser engages in a number of speculative activities that sometimes mean
+    // connections and lookups begin at different times. Workaround that here by
+    // clamping these values to what we expect FetchStart to be.  This means the
+    // later of AsyncOpen or WorkerStart times.
     if (!mAsyncOpen.IsNull()) {
       // We want to clamp to the expected FetchStart value.  This is later of
       // the AsyncOpen and WorkerStart values.
       const TimeStamp* clampTime = &mAsyncOpen;
       if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
         clampTime = &mWorkerStart;
       }
 
@@ -140,52 +138,85 @@ PerformanceTiming::InitializeTimingInfo(
         mSecureConnectionStart = *clampTime;
       }
 
       if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
         mConnectEnd = *clampTime;
       }
     }
   }
+
+  // The aHttpChannel argument is null if this PerformanceTiming object is
+  // being used for navigation timing (which is only relevant for documents).
+  // It has a non-null value if this PerformanceTiming object is being used
+  // for resource timing, which can include document loads, both toplevel and
+  // in subframes, and resources linked from a document.
+  if (aHttpChannel) {
+    mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel);
+    bool redirectsPassCheck = false;
+    aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
+    mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
+
+    SetPropertiesFromHttpChannel(aHttpChannel);
+  }
+}
+
+void
+PerformanceTimingData::SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel)
+{
+  MOZ_ASSERT(aHttpChannel);
+
+  nsAutoCString protocol;
+  Unused << aHttpChannel->GetProtocolVersion(protocol);
+  mNextHopProtocol = NS_ConvertUTF8toUTF16(protocol);
+
+  Unused << aHttpChannel->GetEncodedBodySize(&mEncodedBodySize);
+  Unused << aHttpChannel->GetTransferSize(&mTransferSize);
+  Unused << aHttpChannel->GetDecodedBodySize(&mDecodedBodySize);
+  if (mDecodedBodySize == 0) {
+    mDecodedBodySize = mEncodedBodySize;
+  }
 }
 
 PerformanceTiming::~PerformanceTiming()
 {
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::FetchStartHighRes()
+PerformanceTimingData::FetchStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!mFetchStart) {
     if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return mZeroTime;
     }
     MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
         "valid if the performance timing is enabled");
     if (!mAsyncOpen.IsNull()) {
       if (!mWorkerRequestStart.IsNull() && mWorkerRequestStart > mAsyncOpen) {
-        mFetchStart = TimeStampToDOMHighRes(mWorkerRequestStart);
+        mFetchStart = TimeStampToDOMHighRes(aPerformance, mWorkerRequestStart);
       } else {
-        mFetchStart = TimeStampToDOMHighRes(mAsyncOpen);
+        mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
       }
     }
   }
   return nsRFPService::ReduceTimePrecisionAsMSecs(mFetchStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::FetchStart()
 {
-  return static_cast<int64_t>(FetchStartHighRes());
+  return static_cast<int64_t>(mTimingData->FetchStartHighRes(mPerformance));
 }
 
 bool
-PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel,
-                                      nsITimedChannel* aChannel)
+PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel,
+                                          nsITimedChannel* aChannel)
 {
   if (!IsInitialized()) {
     return false;
   }
 
   // Check that the current document passes the ckeck.
   nsCOMPtr<nsILoadInfo> loadInfo;
   aResourceChannel->GetLoadInfo(getter_AddRefs(loadInfo));
@@ -203,306 +234,328 @@ PerformanceTiming::CheckAllowedOrigin(ns
   nsCOMPtr<nsIPrincipal> principal = loadInfo->LoadingPrincipal();
 
   // Check if the resource is either same origin as the page that started
   // the load, or if the response contains the proper Timing-Allow-Origin
   // header with the domain of the page that started the load.
   return aChannel->TimingAllowCheck(principal);
 }
 
-bool
-PerformanceTiming::TimingAllowed() const
-{
-  return mTimingAllowed;
-}
-
 uint8_t
-PerformanceTiming::GetRedirectCount() const
+PerformanceTimingData::GetRedirectCount() const
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return 0;
   }
   if (!mAllRedirectsSameOrigin) {
     return 0;
   }
   return mRedirectCount;
 }
 
 bool
-PerformanceTiming::ShouldReportCrossOriginRedirect() const
+PerformanceTimingData::ShouldReportCrossOriginRedirect() const
 {
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return false;
   }
 
   // If the redirect count is 0, or if one of the cross-origin
   // redirects doesn't have the proper Timing-Allow-Origin header,
   // then RedirectStart and RedirectEnd will be set to zero
   return (mRedirectCount != 0) && mReportCrossOriginRedirect;
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::AsyncOpenHighRes()
+PerformanceTimingData::AsyncOpenHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) {
     return mZeroTime;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(TimeStampToDOMHighRes(mAsyncOpen));
+  return nsRFPService::ReduceTimePrecisionAsMSecs(
+           TimeStampToDOMHighRes(aPerformance, mAsyncOpen));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::WorkerStartHighRes()
+PerformanceTimingData::WorkerStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) {
     return mZeroTime;
   }
-  return nsRFPService::ReduceTimePrecisionAsMSecs(TimeStampToDOMHighRes(mWorkerStart));
+  return nsRFPService::ReduceTimePrecisionAsMSecs(
+           TimeStampToDOMHighRes(aPerformance, mWorkerStart));
 }
 
 /**
  * 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()
  * and PerformanceResourceTiming::RedirectStart() to make such verifications.
  *
  * @return a valid timing if the Performance Timing is enabled
  */
 DOMHighResTimeStamp
-PerformanceTiming::RedirectStartHighRes()
+PerformanceTimingData::RedirectStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
-  return TimeStampToReducedDOMHighResOrFetchStart(mRedirectStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RedirectStart()
 {
-  if (!IsInitialized()) {
+  if (!mTimingData->IsInitialized()) {
     return 0;
   }
   // We have to check if all the redirect URIs had the same origin (since there
   // is no check in RedirectStartHighRes())
-  if (mAllRedirectsSameOrigin && mRedirectCount) {
-    return static_cast<int64_t>(RedirectStartHighRes());
+  if (mTimingData->AllRedirectsSameOrigin() &&
+      mTimingData->RedirectCountReal()) {
+    return static_cast<int64_t>(mTimingData->RedirectStartHighRes(mPerformance));
   }
   return 0;
 }
 
 /**
  * RedirectEndHighRes() 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, RedirectEndHighRes() will make
  * no checks for cross-domain redirect. It's up to the consumers of this method
  * (PerformanceTiming::RedirectEnd() and
  * PerformanceResourceTiming::RedirectEnd() to make such verifications.
  *
  * @return a valid timing if the Performance Timing is enabled
  */
 DOMHighResTimeStamp
-PerformanceTiming::RedirectEndHighRes()
+PerformanceTimingData::RedirectEndHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
-  return TimeStampToReducedDOMHighResOrFetchStart(mRedirectEnd);
+  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectEnd);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RedirectEnd()
 {
-  if (!IsInitialized()) {
+  if (!mTimingData->IsInitialized()) {
     return 0;
   }
   // We have to check if all the redirect URIs had the same origin (since there
   // is no check in RedirectEndHighRes())
-  if (mAllRedirectsSameOrigin && mRedirectCount) {
-    return static_cast<int64_t>(RedirectEndHighRes());
+  if (mTimingData->AllRedirectsSameOrigin() &&
+      mTimingData->RedirectCountReal()) {
+    return static_cast<int64_t>(mTimingData->RedirectEndHighRes(mPerformance));
   }
   return 0;
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::DomainLookupStartHighRes()
+PerformanceTimingData::DomainLookupStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
-  return TimeStampToReducedDOMHighResOrFetchStart(mDomainLookupStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance,
+                                                  mDomainLookupStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::DomainLookupStart()
 {
-  return static_cast<int64_t>(DomainLookupStartHighRes());
+  return static_cast<int64_t>(mTimingData->DomainLookupStartHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::DomainLookupEndHighRes()
+PerformanceTimingData::DomainLookupEndHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
-  return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes()
-                                   : nsRFPService::ReduceTimePrecisionAsMSecs(
-                                       TimeStampToDOMHighRes(mDomainLookupEnd));
+  return mDomainLookupEnd.IsNull()
+          ? DomainLookupStartHighRes(aPerformance)
+          : nsRFPService::ReduceTimePrecisionAsMSecs(
+              TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::DomainLookupEnd()
 {
-  return static_cast<int64_t>(DomainLookupEndHighRes());
+  return static_cast<int64_t>(mTimingData->DomainLookupEndHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::ConnectStartHighRes()
+PerformanceTimingData::ConnectStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
-  return mConnectStart.IsNull() ? DomainLookupEndHighRes()
-                                : nsRFPService::ReduceTimePrecisionAsMSecs(
-                                    TimeStampToDOMHighRes(mConnectStart));
+  return mConnectStart.IsNull()
+           ? DomainLookupEndHighRes(aPerformance)
+           : nsRFPService::ReduceTimePrecisionAsMSecs(
+               TimeStampToDOMHighRes(aPerformance, mConnectStart));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectStart()
 {
-  return static_cast<int64_t>(ConnectStartHighRes());
+  return static_cast<int64_t>(mTimingData->ConnectStartHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::SecureConnectionStartHighRes()
+PerformanceTimingData::SecureConnectionStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
   return !mSecureConnection
     ? 0 // We use 0 here, because mZeroTime is sometimes set to the navigation
         // start time.
-    : (mSecureConnectionStart.IsNull() ? mZeroTime
-                                       : nsRFPService::ReduceTimePrecisionAsMSecs(
-                                           TimeStampToDOMHighRes(mSecureConnectionStart)));
+    : (mSecureConnectionStart.IsNull()
+        ? mZeroTime
+        : nsRFPService::ReduceTimePrecisionAsMSecs(
+            TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart)));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::SecureConnectionStart()
 {
-  return static_cast<int64_t>(SecureConnectionStartHighRes());
+  return static_cast<int64_t>(mTimingData->SecureConnectionStartHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::ConnectEndHighRes()
+PerformanceTimingData::ConnectEndHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
-  return mConnectEnd.IsNull() ? ConnectStartHighRes()
-                              : nsRFPService::ReduceTimePrecisionAsMSecs(
-                                  TimeStampToDOMHighRes(mConnectEnd));
+  return mConnectEnd.IsNull()
+           ? ConnectStartHighRes(aPerformance)
+           : nsRFPService::ReduceTimePrecisionAsMSecs(
+               TimeStampToDOMHighRes(aPerformance, mConnectEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ConnectEnd()
 {
-  return static_cast<int64_t>(ConnectEndHighRes());
+  return static_cast<int64_t>(mTimingData->ConnectEndHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::RequestStartHighRes()
+PerformanceTimingData::RequestStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
 
   if (mRequestStart.IsNull()) {
     mRequestStart = mWorkerRequestStart;
   }
 
-  return TimeStampToReducedDOMHighResOrFetchStart(mRequestStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRequestStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::RequestStart()
 {
-  return static_cast<int64_t>(RequestStartHighRes());
+  return static_cast<int64_t>(mTimingData->RequestStartHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::ResponseStartHighRes()
+PerformanceTimingData::ResponseStartHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
   if (mResponseStart.IsNull() ||
      (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) {
     mResponseStart = mCacheReadStart;
   }
 
   if (mResponseStart.IsNull() ||
       (!mRequestStart.IsNull() && mResponseStart < mRequestStart)) {
     mResponseStart = mRequestStart;
   }
-  return TimeStampToReducedDOMHighResOrFetchStart(mResponseStart);
+  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mResponseStart);
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ResponseStart()
 {
-  return static_cast<int64_t>(ResponseStartHighRes());
+  return static_cast<int64_t>(mTimingData->ResponseStartHighRes(mPerformance));
 }
 
 DOMHighResTimeStamp
-PerformanceTiming::ResponseEndHighRes()
+PerformanceTimingData::ResponseEndHighRes(Performance* aPerformance)
 {
+  MOZ_ASSERT(aPerformance);
+
   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
       nsContentUtils::ShouldResistFingerprinting()) {
     return mZeroTime;
   }
   if (mResponseEnd.IsNull() ||
      (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
     mResponseEnd = mCacheReadEnd;
   }
   if (mResponseEnd.IsNull()) {
     mResponseEnd = mWorkerResponseEnd;
   }
   // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
-  return mResponseEnd.IsNull() ? ResponseStartHighRes()
-                               : nsRFPService::ReduceTimePrecisionAsMSecs(
-                                   TimeStampToDOMHighRes(mResponseEnd));
+  return mResponseEnd.IsNull()
+           ? ResponseStartHighRes(aPerformance)
+           : nsRFPService::ReduceTimePrecisionAsMSecs(
+               TimeStampToDOMHighRes(aPerformance, mResponseEnd));
 }
 
 DOMTimeMilliSec
 PerformanceTiming::ResponseEnd()
 {
-  return static_cast<int64_t>(ResponseEndHighRes());
-}
-
-bool
-PerformanceTiming::IsInitialized() const
-{
-  return mInitialized;
+  return static_cast<int64_t>(mTimingData->ResponseEndHighRes(mPerformance));
 }
 
 JSObject*
 PerformanceTiming::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceTimingBinding::Wrap(cx, this, aGivenProto);
 }
 
--- a/dom/performance/PerformanceTiming.h
+++ b/dom/performance/PerformanceTiming.h
@@ -15,16 +15,203 @@
 #include "Performance.h"
 
 class nsIHttpChannel;
 class nsITimedChannel;
 
 namespace mozilla {
 namespace dom {
 
+class PerformanceTimingData final
+{
+public:
+  PerformanceTimingData(nsITimedChannel* aChannel,
+                        nsIHttpChannel* aHttpChannel,
+                        DOMHighResTimeStamp aZeroTime);
+
+  void
+  SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel);
+
+  bool IsInitialized() const
+  {
+    return mInitialized;
+  }
+
+  const nsString& NextHopProtocol() const
+  {
+    return mNextHopProtocol;
+  }
+
+  uint64_t TransferSize() const
+  {
+    return mTimingAllowed ? mTransferSize : 0;
+  }
+
+  uint64_t EncodedBodySize() const
+  {
+    return mTimingAllowed ? mEncodedBodySize : 0;
+  }
+
+  uint64_t DecodedBodySize() const
+  {
+    return mTimingAllowed ? mDecodedBodySize : 0;
+  }
+
+  /**
+   * @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
+  TimeStampToReducedDOMHighResOrFetchStart(Performance* aPerformance,
+                                           TimeStamp aStamp)
+  {
+    MOZ_ASSERT(aPerformance);
+
+    return (!aStamp.IsNull())
+        ? nsRFPService::ReduceTimePrecisionAsMSecs(
+            TimeStampToDOMHighRes(aPerformance, aStamp))
+        : FetchStartHighRes(aPerformance);
+  }
+
+  /**
+   * 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).
+   *
+   * The algorithm operates in 2 steps:
+   * 1. The first step is to subtract the two timestamps: the argument (the
+   * event's timestamp) and the navigation start timestamp. This will result in
+   * a relative timestamp of the event (relative to the navigation start -
+   * window.performance.timing.navigationStart).
+   * 2. The second step is to add any required offset (the mZeroTime). For now,
+   * this offset value is either 0 (for the resource timing), or equal to
+   * "performance.navigationStart" (for navigation timing).
+   * For the resource timing, mZeroTime is set to 0, causing the result to be a
+   * relative time.
+   * For the navigation timing, mZeroTime is set to "performance.navigationStart"
+   * causing the result be an absolute time.
+   *
+   * @param   aStamp
+   *          The TimeStamp recorded for a specific event. This TimeStamp can't
+   *          be null.
+   * @return  number of milliseconds value as one of:
+   * - relative to the navigation start time, time the user has landed on the
+   *   page
+   * - an absolute wall clock time since the unix epoch
+   */
+  inline DOMHighResTimeStamp
+  TimeStampToDOMHighRes(Performance* aPerformance, TimeStamp aStamp) const
+  {
+    MOZ_ASSERT(aPerformance);
+    MOZ_ASSERT(!aStamp.IsNull());
+
+    TimeDuration duration =
+        aStamp - aPerformance->GetDOMTiming()->GetNavigationStartTimeStamp();
+    return duration.ToMilliseconds() + mZeroTime;
+  }
+
+  // The last channel's AsyncOpen time.  This may occur before the FetchStart
+  // in some cases.
+  DOMHighResTimeStamp AsyncOpenHighRes(Performance* aPerformance);
+
+  // High resolution (used by resource timing)
+  DOMHighResTimeStamp WorkerStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp FetchStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp RedirectStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp RedirectEndHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp DomainLookupStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp DomainLookupEndHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp ConnectStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp SecureConnectionStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp ConnectEndHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp RequestStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp ResponseStartHighRes(Performance* aPerformance);
+  DOMHighResTimeStamp ResponseEndHighRes(Performance* aPerformance);
+
+  DOMHighResTimeStamp ZeroTime() const { return mZeroTime; }
+
+  uint8_t RedirectCountReal() const { return mRedirectCount; }
+  uint8_t GetRedirectCount() const;
+
+  bool AllRedirectsSameOrigin() const { return mAllRedirectsSameOrigin; }
+
+  // If this is false the values of redirectStart/End will be 0 This is false if
+  // no redirects occured, or if any of the responses failed the
+  // timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
+  bool ShouldReportCrossOriginRedirect() const;
+
+  // Cached result of CheckAllowedOrigin. If false, security sensitive
+  // attributes of the resourceTiming object will be set to 0
+  bool TimingAllowed() const
+  {
+    return mTimingAllowed;
+  }
+
+private:
+  // 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);
+
+  nsString mNextHopProtocol;
+
+  TimeStamp mAsyncOpen;
+  TimeStamp mRedirectStart;
+  TimeStamp mRedirectEnd;
+  TimeStamp mDomainLookupStart;
+  TimeStamp mDomainLookupEnd;
+  TimeStamp mConnectStart;
+  TimeStamp mSecureConnectionStart;
+  TimeStamp mConnectEnd;
+  TimeStamp mRequestStart;
+  TimeStamp mResponseStart;
+  TimeStamp mCacheReadStart;
+  TimeStamp mResponseEnd;
+  TimeStamp mCacheReadEnd;
+
+  // ServiceWorker interception timing information
+  TimeStamp mWorkerStart;
+  TimeStamp mWorkerRequestStart;
+  TimeStamp mWorkerResponseEnd;
+
+  // This is an offset that will be added to each timing ([ms] resolution).
+  // There are only 2 possible values: (1) logicaly equal to navigationStart
+  // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results
+  // are relative to the navigation start).
+  DOMHighResTimeStamp mZeroTime;
+
+  DOMHighResTimeStamp mFetchStart;
+
+  uint64_t mEncodedBodySize;
+  uint64_t mTransferSize;
+  uint64_t mDecodedBodySize;
+
+  uint8_t mRedirectCount;
+
+  bool mAllRedirectsSameOrigin;
+
+  // If the resourceTiming object should have non-zero redirectStart and
+  // redirectEnd attributes. It is false if there were no redirects, or if any
+  // of the responses didn't pass the timing-allow-check
+  bool mReportCrossOriginRedirect;
+
+  bool mSecureConnection;
+
+  bool mTimingAllowed;
+
+  bool mInitialized;
+};
+
 // Script "performance.timing" object
 class PerformanceTiming final : public nsWrapperCache
 {
 public:
 /**
  * @param   aPerformance
  *          The performance object (the JS parent).
  *          This will allow access to "window.performance.timing" attribute for
@@ -55,67 +242,16 @@ public:
     return mPerformance->GetDOMTiming();
   }
 
   Performance* GetParentObject() const
   {
     return mPerformance;
   }
 
-  /**
-   * @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 TimeStampToReducedDOMHighResOrFetchStart(TimeStamp aStamp)
-  {
-    return (!aStamp.IsNull())
-        ? nsRFPService::ReduceTimePrecisionAsMSecs(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).
-   *
-   * The algorithm operates in 2 steps:
-   * 1. The first step is to subtract the two timestamps: the argument (the
-   * envet's timesramp) and the navigation start timestamp. This will result in
-   * a relative timestamp of the event (relative to the navigation start -
-   * window.performance.timing.navigationStart).
-   * 2. The second step is to add any required offset (the mZeroTime). For now,
-   * this offset value is either 0 (for the resource timing), or equal to
-   * "performance.navigationStart" (for navigation timing).
-   * For the resource timing, mZeroTime is set to 0, causing the result to be a
-   * relative time.
-   * For the navigation timing, mZeroTime is set to "performance.navigationStart"
-   * causing the result be an absolute time.
-   *
-   * @param   aStamp
-   *          The TimeStamp recorded for a specific event. This TimeStamp can't
-   *          be null.
-   * @return  number of milliseconds value as one of:
-   * - relative to the navigation start time, time the user has landed on the
-   * page
-   * - an absolute wall clock time since the unix epoch
-   */
-  inline DOMHighResTimeStamp TimeStampToDOMHighRes(TimeStamp aStamp) const
-  {
-    MOZ_ASSERT(!aStamp.IsNull());
-    TimeDuration duration =
-        aStamp - GetDOMTiming()->GetNavigationStartTimeStamp();
-    return duration.ToMilliseconds() + mZeroTime;
-  }
-
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // PerformanceNavigation WebIDL methods
   DOMTimeMilliSec NavigationStart() const
   {
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
@@ -140,50 +276,16 @@ public:
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
       GetDOMTiming()->GetUnloadEventEnd());
   }
 
-  uint8_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);
-
-  // Cached result of CheckAllowedOrigin. If false, security sensitive
-  // attributes of the resourceTiming object will be set to 0
-  bool TimingAllowed() const;
-
-  // If this is false the values of redirectStart/End will be 0
-  // This is false if no redirects occured, or if any of the responses failed
-  // the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
-  bool ShouldReportCrossOriginRedirect() const;
-
-  // The last channel's AsyncOpen time.  This may occur before the FetchStart
-  // in some cases.
-  DOMHighResTimeStamp AsyncOpenHighRes();
-
-  // High resolution (used by resource timing)
-  DOMHighResTimeStamp WorkerStartHighRes();
-  DOMHighResTimeStamp FetchStartHighRes();
-  DOMHighResTimeStamp RedirectStartHighRes();
-  DOMHighResTimeStamp RedirectEndHighRes();
-  DOMHighResTimeStamp DomainLookupStartHighRes();
-  DOMHighResTimeStamp DomainLookupEndHighRes();
-  DOMHighResTimeStamp ConnectStartHighRes();
-  DOMHighResTimeStamp SecureConnectionStartHighRes();
-  DOMHighResTimeStamp ConnectEndHighRes();
-  DOMHighResTimeStamp RequestStartHighRes();
-  DOMHighResTimeStamp ResponseStartHighRes();
-  DOMHighResTimeStamp ResponseEndHighRes();
-
   // Low resolution (used by navigation timing)
   DOMTimeMilliSec FetchStart();
   DOMTimeMilliSec RedirectStart();
   DOMTimeMilliSec RedirectEnd();
   DOMTimeMilliSec DomainLookupStart();
   DOMTimeMilliSec DomainLookupEnd();
   DOMTimeMilliSec ConnectStart();
   DOMTimeMilliSec SecureConnectionStart();
@@ -267,61 +369,27 @@ public:
     if (!nsContentUtils::IsPerformanceTimingEnabled() ||
         nsContentUtils::ShouldResistFingerprinting()) {
       return 0;
     }
     return nsRFPService::ReduceTimePrecisionAsMSecs(
       GetDOMTiming()->GetTimeToNonBlankPaint());
   }
 
+  PerformanceTimingData* Data() const
+  {
+    return mTimingData.get();
+  }
+
 private:
   ~PerformanceTiming();
 
-  bool IsInitialized() const;
-  void InitializeTimingInfo(nsITimedChannel* aChannel);
-
   bool IsTopLevelContentDocument() const;
 
   RefPtr<Performance> mPerformance;
-  DOMHighResTimeStamp mFetchStart;
 
-  // This is an offset that will be added to each timing ([ms] resolution).
-  // There are only 2 possible values: (1) logicaly equal to navigationStart
-  // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results
-  // are relative to the navigation start).
-  DOMHighResTimeStamp mZeroTime;
-
-  TimeStamp mAsyncOpen;
-  TimeStamp mRedirectStart;
-  TimeStamp mRedirectEnd;
-  TimeStamp mDomainLookupStart;
-  TimeStamp mDomainLookupEnd;
-  TimeStamp mConnectStart;
-  TimeStamp mSecureConnectionStart;
-  TimeStamp mConnectEnd;
-  TimeStamp mRequestStart;
-  TimeStamp mResponseStart;
-  TimeStamp mCacheReadStart;
-  TimeStamp mResponseEnd;
-  TimeStamp mCacheReadEnd;
-
-  // ServiceWorker interception timing information
-  TimeStamp mWorkerStart;
-  TimeStamp mWorkerRequestStart;
-  TimeStamp mWorkerResponseEnd;
-
-  uint8_t mRedirectCount;
-  bool mTimingAllowed;
-  bool mAllRedirectsSameOrigin;
-  bool mInitialized;
-
-  // If the resourceTiming object should have non-zero redirectStart and
-  // redirectEnd attributes. It is false if there were no redirects, or if
-  // any of the responses didn't pass the timing-allow-check
-  bool mReportCrossOriginRedirect;
-
-  bool mSecureConnection;
+  UniquePtr<PerformanceTimingData> mTimingData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PerformanceTiming_h