LCOV - code coverage report
Current view: top level - dom/performance - PerformanceTiming.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 55 175 31.4 %
Date: 2017-07-14 16:53:18 Functions: 6 35 17.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "PerformanceTiming.h"
       8             : #include "mozilla/dom/PerformanceTimingBinding.h"
       9             : #include "mozilla/Telemetry.h"
      10             : #include "nsIDocShell.h"
      11             : #include "nsIDocShellTreeItem.h"
      12             : #include "nsIDocument.h"
      13             : #include "nsITimedChannel.h"
      14             : 
      15             : namespace mozilla {
      16             : namespace dom {
      17             : 
      18           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance)
      19             : 
      20           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef)
      21           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)
      22             : 
      23           2 : PerformanceTiming::PerformanceTiming(Performance* aPerformance,
      24             :                                      nsITimedChannel* aChannel,
      25             :                                      nsIHttpChannel* aHttpChannel,
      26           2 :                                      DOMHighResTimeStamp aZeroTime)
      27             :   : mPerformance(aPerformance),
      28             :     mFetchStart(0.0),
      29             :     mZeroTime(aZeroTime),
      30             :     mRedirectCount(0),
      31             :     mTimingAllowed(true),
      32             :     mAllRedirectsSameOrigin(true),
      33           2 :     mInitialized(!!aChannel),
      34           4 :     mReportCrossOriginRedirect(true)
      35             : {
      36           2 :   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
      37             : 
      38           4 :   if (!nsContentUtils::IsPerformanceTimingEnabled() ||
      39           2 :       nsContentUtils::ShouldResistFingerprinting()) {
      40           0 :     mZeroTime = 0;
      41             :   }
      42             : 
      43             :   // The aHttpChannel argument is null if this PerformanceTiming object is
      44             :   // being used for navigation timing (which is only relevant for documents).
      45             :   // It has a non-null value if this PerformanceTiming object is being used
      46             :   // for resource timing, which can include document loads, both toplevel and
      47             :   // in subframes, and resources linked from a document.
      48           2 :   if (aHttpChannel) {
      49           2 :     mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel);
      50           2 :     bool redirectsPassCheck = false;
      51           2 :     aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
      52           2 :     mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
      53             :   }
      54             : 
      55           2 :   InitializeTimingInfo(aChannel);
      56             : 
      57             :   // Non-null aHttpChannel implies that this PerformanceTiming object is being
      58             :   // used for subresources, which is irrelevant to this probe.
      59           2 :   if (!aHttpChannel &&
      60           2 :       nsContentUtils::IsPerformanceTimingEnabled() &&
      61           0 :       IsTopLevelContentDocument()) {
      62           0 :     Telemetry::Accumulate(Telemetry::TIME_TO_RESPONSE_START_MS,
      63           0 :                           ResponseStartHighRes() - mZeroTime);
      64             :   }
      65           2 : }
      66             : 
      67             : // Copy the timing info from the channel so we don't need to keep the channel
      68             : // alive just to get the timestamps.
      69             : void
      70           2 : PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
      71             : {
      72           2 :   if (aChannel) {
      73           2 :     aChannel->GetAsyncOpen(&mAsyncOpen);
      74           2 :     aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
      75           2 :     aChannel->GetRedirectCount(&mRedirectCount);
      76           2 :     aChannel->GetRedirectStart(&mRedirectStart);
      77           2 :     aChannel->GetRedirectEnd(&mRedirectEnd);
      78           2 :     aChannel->GetDomainLookupStart(&mDomainLookupStart);
      79           2 :     aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
      80           2 :     aChannel->GetConnectStart(&mConnectStart);
      81           2 :     aChannel->GetConnectEnd(&mConnectEnd);
      82           2 :     aChannel->GetRequestStart(&mRequestStart);
      83           2 :     aChannel->GetResponseStart(&mResponseStart);
      84           2 :     aChannel->GetCacheReadStart(&mCacheReadStart);
      85           2 :     aChannel->GetResponseEnd(&mResponseEnd);
      86           2 :     aChannel->GetCacheReadEnd(&mCacheReadEnd);
      87             :   }
      88           2 : }
      89             : 
      90           0 : PerformanceTiming::~PerformanceTiming()
      91             : {
      92           0 : }
      93             : 
      94             : DOMHighResTimeStamp
      95           2 : PerformanceTiming::FetchStartHighRes()
      96             : {
      97           2 :   if (!mFetchStart) {
      98           4 :     if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
      99           2 :         nsContentUtils::ShouldResistFingerprinting()) {
     100           0 :       return mZeroTime;
     101             :     }
     102           2 :     MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
     103             :         "valid if the performance timing is enabled");
     104           4 :     mFetchStart = (!mAsyncOpen.IsNull())
     105           2 :         ? TimeStampToDOMHighRes(mAsyncOpen)
     106             :         : 0.0;
     107             :   }
     108           2 :   return mFetchStart;
     109             : }
     110             : 
     111             : DOMTimeMilliSec
     112           0 : PerformanceTiming::FetchStart()
     113             : {
     114           0 :   return static_cast<int64_t>(FetchStartHighRes());
     115             : }
     116             : 
     117             : bool
     118           2 : PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel,
     119             :                                       nsITimedChannel* aChannel)
     120             : {
     121           2 :   if (!IsInitialized()) {
     122           0 :     return false;
     123             :   }
     124             : 
     125             :   // Check that the current document passes the ckeck.
     126           4 :   nsCOMPtr<nsILoadInfo> loadInfo;
     127           2 :   aResourceChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     128           2 :   if (!loadInfo) {
     129           0 :     return false;
     130             :   }
     131             : 
     132             :   // TYPE_DOCUMENT loads have no loadingPrincipal.  And that's OK, because we
     133             :   // never actually need to have a performance timing entry for TYPE_DOCUMENT
     134             :   // loads.
     135           2 :   if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
     136           0 :     return false;
     137             :   }
     138             : 
     139           4 :   nsCOMPtr<nsIPrincipal> principal = loadInfo->LoadingPrincipal();
     140             : 
     141             :   // Check if the resource is either same origin as the page that started
     142             :   // the load, or if the response contains the proper Timing-Allow-Origin
     143             :   // header with the domain of the page that started the load.
     144           2 :   return aChannel->TimingAllowCheck(principal);
     145             : }
     146             : 
     147             : bool
     148           0 : PerformanceTiming::TimingAllowed() const
     149             : {
     150           0 :   return mTimingAllowed;
     151             : }
     152             : 
     153             : uint16_t
     154           0 : PerformanceTiming::GetRedirectCount() const
     155             : {
     156           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     157           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     158           0 :     return 0;
     159             :   }
     160           0 :   if (!mAllRedirectsSameOrigin) {
     161           0 :     return 0;
     162             :   }
     163           0 :   return mRedirectCount;
     164             : }
     165             : 
     166             : bool
     167           0 : PerformanceTiming::ShouldReportCrossOriginRedirect() const
     168             : {
     169           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     170           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     171           0 :     return false;
     172             :   }
     173             : 
     174             :   // If the redirect count is 0, or if one of the cross-origin
     175             :   // redirects doesn't have the proper Timing-Allow-Origin header,
     176             :   // then RedirectStart and RedirectEnd will be set to zero
     177           0 :   return (mRedirectCount != 0) && mReportCrossOriginRedirect;
     178             : }
     179             : 
     180             : /**
     181             :  * RedirectStartHighRes() is used by both the navigation timing and the
     182             :  * resource timing. Since, navigation timing and resource timing check and
     183             :  * interpret cross-domain redirects in a different manner,
     184             :  * RedirectStartHighRes() will make no checks for cross-domain redirect.
     185             :  * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
     186             :  * and PerformanceResourceTiming::RedirectStart() to make such verifications.
     187             :  *
     188             :  * @return a valid timing if the Performance Timing is enabled
     189             :  */
     190             : DOMHighResTimeStamp
     191           2 : PerformanceTiming::RedirectStartHighRes()
     192             : {
     193           4 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     194           2 :       nsContentUtils::ShouldResistFingerprinting()) {
     195           0 :     return mZeroTime;
     196             :   }
     197           2 :   return TimeStampToDOMHighResOrFetchStart(mRedirectStart);
     198             : }
     199             : 
     200             : DOMTimeMilliSec
     201           0 : PerformanceTiming::RedirectStart()
     202             : {
     203           0 :   if (!IsInitialized()) {
     204           0 :     return 0;
     205             :   }
     206             :   // We have to check if all the redirect URIs had the same origin (since there
     207             :   // is no check in RedirectStartHighRes())
     208           0 :   if (mAllRedirectsSameOrigin && mRedirectCount) {
     209           0 :     return static_cast<int64_t>(RedirectStartHighRes());
     210             :   }
     211           0 :   return 0;
     212             : }
     213             : 
     214             : /**
     215             :  * RedirectEndHighRes() is used by both the navigation timing and the resource
     216             :  * timing. Since, navigation timing and resource timing check and interpret
     217             :  * cross-domain redirects in a different manner, RedirectEndHighRes() will make
     218             :  * no checks for cross-domain redirect. It's up to the consumers of this method
     219             :  * (PerformanceTiming::RedirectEnd() and
     220             :  * PerformanceResourceTiming::RedirectEnd() to make such verifications.
     221             :  *
     222             :  * @return a valid timing if the Performance Timing is enabled
     223             :  */
     224             : DOMHighResTimeStamp
     225           0 : PerformanceTiming::RedirectEndHighRes()
     226             : {
     227           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     228           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     229           0 :     return mZeroTime;
     230             :   }
     231           0 :   return TimeStampToDOMHighResOrFetchStart(mRedirectEnd);
     232             : }
     233             : 
     234             : DOMTimeMilliSec
     235           0 : PerformanceTiming::RedirectEnd()
     236             : {
     237           0 :   if (!IsInitialized()) {
     238           0 :     return 0;
     239             :   }
     240             :   // We have to check if all the redirect URIs had the same origin (since there
     241             :   // is no check in RedirectEndHighRes())
     242           0 :   if (mAllRedirectsSameOrigin && mRedirectCount) {
     243           0 :     return static_cast<int64_t>(RedirectEndHighRes());
     244             :   }
     245           0 :   return 0;
     246             : }
     247             : 
     248             : DOMHighResTimeStamp
     249           0 : PerformanceTiming::DomainLookupStartHighRes()
     250             : {
     251           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     252           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     253           0 :     return mZeroTime;
     254             :   }
     255           0 :   return TimeStampToDOMHighResOrFetchStart(mDomainLookupStart);
     256             : }
     257             : 
     258             : DOMTimeMilliSec
     259           0 : PerformanceTiming::DomainLookupStart()
     260             : {
     261           0 :   return static_cast<int64_t>(DomainLookupStartHighRes());
     262             : }
     263             : 
     264             : DOMHighResTimeStamp
     265           0 : PerformanceTiming::DomainLookupEndHighRes()
     266             : {
     267           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     268           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     269           0 :     return mZeroTime;
     270             :   }
     271             :   // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
     272           0 :   return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes()
     273           0 :                                    : TimeStampToDOMHighRes(mDomainLookupEnd);
     274             : }
     275             : 
     276             : DOMTimeMilliSec
     277           0 : PerformanceTiming::DomainLookupEnd()
     278             : {
     279           0 :   return static_cast<int64_t>(DomainLookupEndHighRes());
     280             : }
     281             : 
     282             : DOMHighResTimeStamp
     283           0 : PerformanceTiming::ConnectStartHighRes()
     284             : {
     285           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     286           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     287           0 :     return mZeroTime;
     288             :   }
     289           0 :   return mConnectStart.IsNull() ? DomainLookupEndHighRes()
     290           0 :                                 : TimeStampToDOMHighRes(mConnectStart);
     291             : }
     292             : 
     293             : DOMTimeMilliSec
     294           0 : PerformanceTiming::ConnectStart()
     295             : {
     296           0 :   return static_cast<int64_t>(ConnectStartHighRes());
     297             : }
     298             : 
     299             : DOMHighResTimeStamp
     300           0 : PerformanceTiming::ConnectEndHighRes()
     301             : {
     302           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     303           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     304           0 :     return mZeroTime;
     305             :   }
     306             :   // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
     307           0 :   return mConnectEnd.IsNull() ? ConnectStartHighRes()
     308           0 :                               : TimeStampToDOMHighRes(mConnectEnd);
     309             : }
     310             : 
     311             : DOMTimeMilliSec
     312           0 : PerformanceTiming::ConnectEnd()
     313             : {
     314           0 :   return static_cast<int64_t>(ConnectEndHighRes());
     315             : }
     316             : 
     317             : DOMHighResTimeStamp
     318           0 : PerformanceTiming::RequestStartHighRes()
     319             : {
     320           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     321           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     322           0 :     return mZeroTime;
     323             :   }
     324           0 :   return TimeStampToDOMHighResOrFetchStart(mRequestStart);
     325             : }
     326             : 
     327             : DOMTimeMilliSec
     328           0 : PerformanceTiming::RequestStart()
     329             : {
     330           0 :   return static_cast<int64_t>(RequestStartHighRes());
     331             : }
     332             : 
     333             : DOMHighResTimeStamp
     334           0 : PerformanceTiming::ResponseStartHighRes()
     335             : {
     336           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     337           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     338           0 :     return mZeroTime;
     339             :   }
     340           0 :   if (mResponseStart.IsNull() ||
     341           0 :      (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) {
     342           0 :     mResponseStart = mCacheReadStart;
     343             :   }
     344           0 :   return TimeStampToDOMHighResOrFetchStart(mResponseStart);
     345             : }
     346             : 
     347             : DOMTimeMilliSec
     348           0 : PerformanceTiming::ResponseStart()
     349             : {
     350           0 :   return static_cast<int64_t>(ResponseStartHighRes());
     351             : }
     352             : 
     353             : DOMHighResTimeStamp
     354           0 : PerformanceTiming::ResponseEndHighRes()
     355             : {
     356           0 :   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
     357           0 :       nsContentUtils::ShouldResistFingerprinting()) {
     358           0 :     return mZeroTime;
     359             :   }
     360           0 :   if (mResponseEnd.IsNull() ||
     361           0 :      (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
     362           0 :     mResponseEnd = mCacheReadEnd;
     363             :   }
     364             :   // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
     365           0 :   return mResponseEnd.IsNull() ? ResponseStartHighRes()
     366           0 :                                : TimeStampToDOMHighRes(mResponseEnd);
     367             : }
     368             : 
     369             : DOMTimeMilliSec
     370           0 : PerformanceTiming::ResponseEnd()
     371             : {
     372           0 :   return static_cast<int64_t>(ResponseEndHighRes());
     373             : }
     374             : 
     375             : bool
     376           6 : PerformanceTiming::IsInitialized() const
     377             : {
     378           6 :   return mInitialized;
     379             : }
     380             : 
     381             : JSObject*
     382           0 : PerformanceTiming::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
     383             : {
     384           0 :   return PerformanceTimingBinding::Wrap(cx, this, aGivenProto);
     385             : }
     386             : 
     387             : bool
     388           0 : PerformanceTiming::IsTopLevelContentDocument() const
     389             : {
     390           0 :   nsCOMPtr<nsIDocument> document = mPerformance->GetDocumentIfCurrent();
     391           0 :   if (!document) {
     392           0 :     return false;
     393             :   }
     394           0 :   nsCOMPtr<nsIDocShell> docShell = document->GetDocShell();
     395           0 :   if (!docShell) {
     396           0 :     return false;
     397             :   }
     398           0 :   nsCOMPtr<nsIDocShellTreeItem> rootItem;
     399           0 :   Unused << docShell->GetSameTypeRootTreeItem(getter_AddRefs(rootItem));
     400           0 :   if (rootItem.get() != static_cast<nsIDocShellTreeItem*>(docShell.get())) {
     401           0 :     return false;
     402             :   }
     403           0 :   return rootItem->ItemType() == nsIDocShellTreeItem::typeContent;
     404             : }
     405             : 
     406             : } // dom namespace
     407             : } // mozilla namespace

Generated by: LCOV version 1.13