Bug 1423495 - Part4: Create doc entry form http channel if server timing headers are found for a document load draft
authorKershaw Chang <kechang@mozilla.com>, Valentin Gosu <valentin.gosu@gmail.com>
Fri, 12 Jan 2018 03:13:00 +0100
changeset 786592 e2523eb018839e0f2b2b1944bce4bbe77a3cc84b
parent 786591 9c7a81db01758e4f6e35236a5e7ce40557c9f2a1
child 786593 6861261b3b05e8ba378412e141c3a8cc029afa9d
push id107535
push uservalentin.gosu@gmail.com
push dateMon, 23 Apr 2018 16:35:48 +0000
bugs1423495
milestone61.0a1
Bug 1423495 - Part4: Create doc entry form http channel if server timing headers are found for a document load Currently, the document entry is created at the first time when some JS code tries to access it. But for the case when server timing headers exist for a document loading channel, we need to create the document entry and save the server timing data in the document entry. If we don’t do this, the server timing data would be lost since the http channel will be deleted. MozReview-Commit-ID: B5ksAZvZACq
dom/performance/PerformanceMainThread.cpp
dom/performance/PerformanceMainThread.h
dom/performance/PerformanceStorage.h
dom/performance/PerformanceStorageWorker.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/InterceptedHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.cpp
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -292,16 +292,30 @@ PerformanceMainThread::EnsureDocEntry()
     if (httpChannel) {
       timing->SetPropertiesFromHttpChannel(httpChannel);
     }
 
     mDocEntry = new PerformanceNavigationTiming(Move(timing), this);
   }
 }
 
+void
+PerformanceMainThread::CreateDocumentEntry(nsITimedChannel* aChannel)
+{
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(!mDocEntry, "mDocEntry should be null.");
+
+  if (!nsContentUtils::IsPerformanceNavigationTimingEnabled()) {
+    return;
+  }
+
+  UniquePtr<PerformanceTimingData> timing(
+      new PerformanceTimingData(aChannel, nullptr, 0));
+  mDocEntry = new PerformanceNavigationTiming(Move(timing), this);
+}
 
 void
 PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
 {
   // We return an empty list when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     aRetval.Clear();
     return;
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -32,16 +32,18 @@ public:
 
   virtual PerformanceTiming* Timing() override;
 
   virtual PerformanceNavigation* Navigation() override;
 
   virtual void AddEntry(nsIHttpChannel* channel,
                         nsITimedChannel* timedChannel) override;
 
+  void CreateDocumentEntry(nsITimedChannel* aChannel) override;
+
   TimeStamp CreationTimeStamp() const override;
 
   DOMHighResTimeStamp CreationTime() const override;
 
   virtual void GetMozMemory(JSContext *aCx,
                             JS::MutableHandle<JSObject*> aObj) override;
 
   virtual nsDOMNavigationTiming* GetDOMTiming() const override
--- a/dom/performance/PerformanceStorage.h
+++ b/dom/performance/PerformanceStorage.h
@@ -20,16 +20,18 @@ class PerformanceTimingData;
 class PerformanceStorage
 {
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void AddEntry(nsIHttpChannel* aChannel,
                         nsITimedChannel* aTimedChannel) = 0;
 
+  virtual void CreateDocumentEntry(nsITimedChannel* aChannel) = 0;
+
 protected:
   virtual ~PerformanceStorage() {}
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PerformanceStorage_h
--- a/dom/performance/PerformanceStorageWorker.h
+++ b/dom/performance/PerformanceStorageWorker.h
@@ -25,16 +25,21 @@ public:
   static already_AddRefed<PerformanceStorageWorker>
   Create(WorkerPrivate* aWorkerPrivate);
 
   void ShutdownOnWorker();
 
   void AddEntry(nsIHttpChannel* aChannel,
                 nsITimedChannel* aTimedChannel) override;
 
+  void CreateDocumentEntry(nsITimedChannel* aChannel) override
+  {
+    MOZ_CRASH("This should not be called on workers.");
+  }
+
   void AddEntryOnWorker(UniquePtr<PerformanceProxyData>&& aData);
 
 private:
   PerformanceStorageWorker();
   ~PerformanceStorageWorker();
 
   Mutex mMutex;
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -4245,21 +4245,16 @@ HttpBaseChannel::GetPerformanceStorage()
   }
 
   // If a custom performance storage is set, let's use it.
   mozilla::dom::PerformanceStorage* performanceStorage = mLoadInfo->GetPerformanceStorage();
   if (performanceStorage) {
     return performanceStorage;
   }
 
-  // We don't need to report the resource timing entry for a TYPE_DOCUMENT load.
-  if (mLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
-    return nullptr;
-  }
-
   nsCOMPtr<nsIDOMDocument> domDocument;
   mLoadInfo->GetLoadingDocument(getter_AddRefs(domDocument));
   if (!domDocument) {
     return nullptr;
   }
 
   nsCOMPtr<nsIDocument> loadingDocument = do_QueryInterface(domDocument);
   if (!loadingDocument) {
@@ -4282,16 +4277,41 @@ HttpBaseChannel::GetPerformanceStorage()
   mozilla::dom::Performance* performance = innerWindow->GetPerformance();
   if (!performance) {
     return nullptr;
   }
 
   return performance->AsPerformanceStorage();
 }
 
+void
+HttpBaseChannel::MaybeReportTimingData()
+{
+  // We don't need to report the resource timing entry for a TYPE_DOCUMENT load.
+  // But for the case that Server-Timing headers are existed for
+  // a document load, we have to create the document entry early
+  // with the timed channel. This is the only way to make
+  // server timing data availeble in the document entry.
+  if (mLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
+    if ((mResponseHead && mResponseHead->HasHeader(nsHttp::Server_Timing)) ||
+        (mResponseTrailers && mResponseTrailers->HasHeader(nsHttp::Server_Timing))) {
+      mozilla::dom::PerformanceStorage* documentPerformance = GetPerformanceStorage();
+      if (documentPerformance) {
+        documentPerformance->CreateDocumentEntry(this);
+      }
+    }
+    return;
+  }
+
+  mozilla::dom::PerformanceStorage* documentPerformance = GetPerformanceStorage();
+  if (documentPerformance) {
+      documentPerformance->AddEntry(this, this);
+  }
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::SetReportResourceTiming(bool enabled) {
   mReportTiming = enabled;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetReportResourceTiming(bool* _retval) {
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -424,16 +424,17 @@ protected:
 
   // This is fired only when a cookie is created due to the presence of
   // Set-Cookie header in the response header of any network request.
   // This notification will come only after the "http-on-examine-response"
   // was fired.
   void NotifySetCookie(char const *aCookie);
 
   mozilla::dom::PerformanceStorage* GetPerformanceStorage();
+  void MaybeReportTimingData();
   nsIURI* GetReferringPage();
   nsPIDOMWindowInner* GetInnerDOMWindow();
 
   void AddCookiesToRequest();
   virtual MOZ_MUST_USE nsresult
   SetupReplacementChannel(nsIURI *, nsIChannel *, bool preserveMethod,
                           uint32_t redirectFlags);
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1208,20 +1208,17 @@ void
 HttpChannelChild::DoPreOnStopRequest(nsresult aStatus)
 {
   LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
        this, static_cast<uint32_t>(aStatus)));
   mIsPending = false;
 
   MaybeCallSynthesizedCallback();
 
-  PerformanceStorage* performanceStorage = GetPerformanceStorage();
-  if (performanceStorage) {
-      performanceStorage->AddEntry(this, this);
-  }
+  MaybeReportTimingData();
 
   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
     mStatus = aStatus;
   }
 
   CollectOMTTelemetry();
 }
 
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -1079,20 +1079,17 @@ InterceptedHttpChannel::OnStopRequest(ns
   // progress when OnStopRequest() is triggered.  Report any left over
   // progress immediately.  The extra runnable will then do nothing thanks
   // to the ReleaseListeners() call below.
   MaybeCallStatusAndProgress();
 
   mIsPending = false;
 
   // Register entry to the PerformanceStorage resource timing
-  mozilla::dom::PerformanceStorage* performanceStorage = GetPerformanceStorage();
-  if (performanceStorage) {
-    performanceStorage->AddEntry(this, this);
-  }
+  MaybeReportTimingData();
 
   if (mListener) {
     mListener->OnStopRequest(this, mListenerContext, mStatus);
   }
 
   gHttpHandler->OnStopRequest(this);
 
   ReleaseListeners();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7426,20 +7426,17 @@ nsHttpChannel::OnStopRequest(nsIRequest 
                      static_cast<uint32_t>(rv)));
             }
         }
     }
 
     ReportRcwnStats(isFromNet);
 
     // Register entry to the PerformanceStorage resource timing
-    mozilla::dom::PerformanceStorage* performanceStorage = GetPerformanceStorage();
-    if (performanceStorage) {
-        performanceStorage->AddEntry(this, this);
-    }
+    MaybeReportTimingData();
 
     if (mListener) {
         LOG(("nsHttpChannel %p calling OnStopRequest\n", this));
         MOZ_ASSERT(mOnStartRequestCalled,
                    "OnStartRequest should be called before OnStopRequest");
         MOZ_ASSERT(!mOnStopRequestCalled,
                    "We should not call OnStopRequest twice");
         mListener->OnStopRequest(this, mListenerContext, status);