tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PerformanceTiming.cpp (29503B)


      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 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/StaticPrefs_dom.h"
     11 #include "mozilla/dom/BrowsingContext.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/FragmentDirective.h"
     14 #include "mozilla/dom/PerformanceResourceTimingBinding.h"
     15 #include "mozilla/dom/PerformanceTimingBinding.h"
     16 #include "mozilla/glean/DomPerformanceMetrics.h"
     17 #include "nsIDocShell.h"
     18 #include "nsIDocShellTreeItem.h"
     19 #include "nsIHttpChannel.h"
     20 #include "nsITimedChannel.h"
     21 
     22 namespace mozilla::dom {
     23 
     24 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance)
     25 
     26 /* static */
     27 PerformanceTimingData* PerformanceTimingData::Create(
     28    nsITimedChannel* aTimedChannel, nsIHttpChannel* aChannel,
     29    DOMHighResTimeStamp aZeroTime, nsAString& aInitiatorType,
     30    nsAString& aEntryName) {
     31  MOZ_ASSERT(NS_IsMainThread());
     32 
     33  // Check if resource timing is prefed off.
     34  if (!StaticPrefs::dom_enable_resource_timing()) {
     35    return nullptr;
     36  }
     37 
     38  if (!aChannel || !aTimedChannel) {
     39    return nullptr;
     40  }
     41 
     42  bool reportTiming = true;
     43  aTimedChannel->GetReportResourceTiming(&reportTiming);
     44 
     45  if (!reportTiming) {
     46    return nullptr;
     47  }
     48 
     49  aTimedChannel->GetInitiatorType(aInitiatorType);
     50 
     51  // If the initiator type had no valid value, then set it to the default
     52  // ("other") value.
     53  if (aInitiatorType.IsEmpty()) {
     54    aInitiatorType = u"other"_ns;
     55  }
     56 
     57  // According to the spec, "The name attribute must return the resolved URL
     58  // of the requested resource. This attribute must not change even if the
     59  // fetch redirected to a different URL."
     60  nsCOMPtr<nsIURI> originalURI;
     61  aChannel->GetOriginalURI(getter_AddRefs(originalURI));
     62 
     63  nsAutoCString name;
     64  FragmentDirective::GetSpecIgnoringFragmentDirective(originalURI, name);
     65  CopyUTF8toUTF16(name, aEntryName);
     66 
     67  // The nsITimedChannel argument will be used to gather all the timings.
     68  // The nsIHttpChannel argument will be used to check if any cross-origin
     69  // redirects occurred.
     70  // The last argument is the "zero time" (offset). Since we don't want
     71  // any offset for the resource timing, this will be set to "0" - the
     72  // resource timing returns a relative timing (no offset).
     73  return new PerformanceTimingData(aTimedChannel, aChannel, 0);
     74 }
     75 
     76 /* static */
     77 PerformanceTimingData* PerformanceTimingData::Create(
     78    const CacheablePerformanceTimingData& aCachedData,
     79    DOMHighResTimeStamp aZeroTime, TimeStamp aStartTime, TimeStamp aEndTime,
     80    RenderBlockingStatusType aRenderBlockingStatus) {
     81  MOZ_ASSERT(NS_IsMainThread());
     82 
     83  // Check if resource timing is prefed off.
     84  if (!StaticPrefs::dom_enable_resource_timing()) {
     85    return nullptr;
     86  }
     87 
     88  return new PerformanceTimingData(aCachedData, aZeroTime, aStartTime, aEndTime,
     89                                   aRenderBlockingStatus);
     90 }
     91 
     92 PerformanceTiming::PerformanceTiming(Performance* aPerformance,
     93                                     nsITimedChannel* aChannel,
     94                                     nsIHttpChannel* aHttpChannel,
     95                                     DOMHighResTimeStamp aZeroTime)
     96    : mPerformance(aPerformance) {
     97  MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
     98 
     99  mTimingData.reset(new PerformanceTimingData(
    100      aChannel, aHttpChannel,
    101      nsRFPService::ReduceTimePrecisionAsMSecs(
    102          aZeroTime, aPerformance->GetRandomTimelineSeed(),
    103          aPerformance->GetRTPCallerType())));
    104 
    105  // Non-null aHttpChannel implies that this PerformanceTiming object is being
    106  // used for subresources, which is irrelevant to this probe.
    107  if (!aHttpChannel && StaticPrefs::dom_enable_performance() &&
    108      IsTopLevelContentDocument()) {
    109    glean::performance_time::response_start.AccumulateRawDuration(
    110        TimeDuration::FromMilliseconds(
    111            mTimingData->ResponseStartHighRes(aPerformance) -
    112            mTimingData->ZeroTime()));
    113  }
    114 }
    115 
    116 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
    117    nsITimedChannel* aChannel, nsIHttpChannel* aHttpChannel)
    118    : mEncodedBodySize(0),
    119      mDecodedBodySize(0),
    120      mResponseStatus(0),
    121      mRedirectCount(0),
    122      mAllRedirectsSameOrigin(true),
    123      mAllRedirectsPassTAO(true),
    124      mSecureConnection(false),
    125      mTimingAllowed(true),
    126      mInitialized(false) {
    127  mInitialized = !!aChannel;
    128 
    129  nsCOMPtr<nsIURI> uri;
    130  if (aHttpChannel) {
    131    aHttpChannel->GetURI(getter_AddRefs(uri));
    132  } else {
    133    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    134    if (httpChannel) {
    135      httpChannel->GetURI(getter_AddRefs(uri));
    136    }
    137  }
    138 
    139  if (uri) {
    140    mSecureConnection = uri->SchemeIs("https");
    141  }
    142 
    143  if (aChannel) {
    144    aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
    145    aChannel->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO);
    146    aChannel->GetRedirectCount(&mRedirectCount);
    147  }
    148 
    149  // The aHttpChannel argument is null if this PerformanceTiming object is
    150  // being used for navigation timing (which is only relevant for documents).
    151  // It has a non-null value if this PerformanceTiming object is being used
    152  // for resource timing, which can include document loads, both toplevel and
    153  // in subframes, and resources linked from a document.
    154  if (aHttpChannel) {
    155    SetCacheablePropertiesFromHttpChannel(aHttpChannel, aChannel);
    156  }
    157 }
    158 
    159 // Copy the timing info from the channel so we don't need to keep the channel
    160 // alive just to get the timestamps.
    161 PerformanceTimingData::PerformanceTimingData(nsITimedChannel* aChannel,
    162                                             nsIHttpChannel* aHttpChannel,
    163                                             DOMHighResTimeStamp aZeroTime)
    164    : CacheablePerformanceTimingData(aChannel, aHttpChannel),
    165      mZeroTime(0.0),
    166      mFetchStart(0.0),
    167      mTransferSize(0) {
    168  mZeroTime = aZeroTime;
    169 
    170  if (!StaticPrefs::dom_enable_performance()) {
    171    mZeroTime = 0;
    172  }
    173 
    174  if (aChannel) {
    175    aChannel->GetAsyncOpen(&mAsyncOpen);
    176    aChannel->GetRedirectStart(&mRedirectStart);
    177    aChannel->GetRedirectEnd(&mRedirectEnd);
    178    aChannel->GetDomainLookupStart(&mDomainLookupStart);
    179    aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
    180    aChannel->GetConnectStart(&mConnectStart);
    181    aChannel->GetSecureConnectionStart(&mSecureConnectionStart);
    182    aChannel->GetConnectEnd(&mConnectEnd);
    183    aChannel->GetRequestStart(&mRequestStart);
    184    aChannel->GetResponseStart(&mResponseStart);
    185    aChannel->GetCacheReadStart(&mCacheReadStart);
    186    aChannel->GetResponseEnd(&mResponseEnd);
    187    aChannel->GetCacheReadEnd(&mCacheReadEnd);
    188 
    189    aChannel->GetDispatchFetchEventStart(&mWorkerStart);
    190    aChannel->GetHandleFetchEventStart(&mWorkerRequestStart);
    191    // TODO: Track when FetchEvent.respondWith() promise resolves as
    192    //       ServiceWorker interception responseStart?
    193    aChannel->GetHandleFetchEventEnd(&mWorkerResponseEnd);
    194 
    195    // The performance timing api essentially requires that the event timestamps
    196    // have a strict relation with each other. The truth, however, is the
    197    // browser engages in a number of speculative activities that sometimes mean
    198    // connections and lookups begin at different times. Workaround that here by
    199    // clamping these values to what we expect FetchStart to be.  This means the
    200    // later of AsyncOpen or WorkerStart times.
    201    if (!mAsyncOpen.IsNull()) {
    202      // We want to clamp to the expected FetchStart value.  This is later of
    203      // the AsyncOpen and WorkerStart values.
    204      const TimeStamp* clampTime = &mAsyncOpen;
    205      if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
    206        clampTime = &mWorkerStart;
    207      }
    208 
    209      if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
    210        mDomainLookupStart = *clampTime;
    211      }
    212 
    213      if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
    214        mDomainLookupEnd = *clampTime;
    215      }
    216 
    217      if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
    218        mConnectStart = *clampTime;
    219      }
    220 
    221      if (mSecureConnection && !mSecureConnectionStart.IsNull() &&
    222          mSecureConnectionStart < *clampTime) {
    223        mSecureConnectionStart = *clampTime;
    224      }
    225 
    226      if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
    227        mConnectEnd = *clampTime;
    228      }
    229    }
    230  }
    231 
    232  if (aHttpChannel) {
    233    // NOTE: Other fields are set by SetCacheablePropertiesFromHttpChannel,
    234    // called inside CacheablePerformanceTimingData constructor.
    235    SetTransferSizeFromHttpChannel(aHttpChannel);
    236  }
    237 
    238  bool renderBlocking = false;
    239  if (aChannel) {
    240    aChannel->GetRenderBlocking(&renderBlocking);
    241  }
    242  mRenderBlockingStatus = renderBlocking
    243                              ? RenderBlockingStatusType::Blocking
    244                              : RenderBlockingStatusType::Non_blocking;
    245 }
    246 
    247 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
    248    const CacheablePerformanceTimingData& aOther)
    249    : mEncodedBodySize(aOther.mEncodedBodySize),
    250      mDecodedBodySize(aOther.mDecodedBodySize),
    251      mResponseStatus(aOther.mResponseStatus),
    252      mRedirectCount(aOther.mRedirectCount),
    253      mBodyInfoAccessAllowed(aOther.mBodyInfoAccessAllowed),
    254      mAllRedirectsSameOrigin(aOther.mAllRedirectsSameOrigin),
    255      mAllRedirectsPassTAO(aOther.mAllRedirectsPassTAO),
    256      mSecureConnection(aOther.mSecureConnection),
    257      mTimingAllowed(aOther.mTimingAllowed),
    258      mInitialized(aOther.mInitialized),
    259      mNextHopProtocol(aOther.mNextHopProtocol),
    260      mContentType(aOther.mContentType) {
    261  for (auto& data : aOther.mServerTiming) {
    262    mServerTiming.AppendElement(data);
    263  }
    264 }
    265 
    266 PerformanceTimingData::PerformanceTimingData(
    267    const CacheablePerformanceTimingData& aCachedData,
    268    DOMHighResTimeStamp aZeroTime, TimeStamp aStartTime, TimeStamp aEndTime,
    269    RenderBlockingStatusType aRenderBlockingStatus)
    270    : CacheablePerformanceTimingData(aCachedData),
    271      mAsyncOpen(aStartTime),
    272      mResponseEnd(aEndTime),
    273      mZeroTime(aZeroTime),
    274      mTransferSize(kLocalCacheTransferSize),
    275      mRenderBlockingStatus(aRenderBlockingStatus) {
    276  if (!StaticPrefs::dom_enable_performance()) {
    277    mZeroTime = 0;
    278  }
    279 }
    280 
    281 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
    282    const IPCPerformanceTimingData& aIPCData)
    283    : mEncodedBodySize(aIPCData.encodedBodySize()),
    284      mDecodedBodySize(aIPCData.decodedBodySize()),
    285      mResponseStatus(aIPCData.responseStatus()),
    286      mRedirectCount(aIPCData.redirectCount()),
    287      mBodyInfoAccessAllowed(aIPCData.bodyInfoAccessAllowed()),
    288      mAllRedirectsSameOrigin(aIPCData.allRedirectsSameOrigin()),
    289      mAllRedirectsPassTAO(aIPCData.allRedirectsPassTAO()),
    290      mSecureConnection(aIPCData.secureConnection()),
    291      mTimingAllowed(aIPCData.timingAllowed()),
    292      mInitialized(aIPCData.initialized()),
    293      mNextHopProtocol(aIPCData.nextHopProtocol()),
    294      mContentType(aIPCData.contentType()) {
    295  for (const auto& serverTimingData : aIPCData.serverTiming()) {
    296    RefPtr<nsServerTiming> timing = new nsServerTiming();
    297    timing->SetName(serverTimingData.name());
    298    timing->SetDuration(serverTimingData.duration());
    299    timing->SetDescription(serverTimingData.description());
    300    mServerTiming.AppendElement(timing);
    301  }
    302 }
    303 
    304 PerformanceTimingData::PerformanceTimingData(
    305    const IPCPerformanceTimingData& aIPCData)
    306    : CacheablePerformanceTimingData(aIPCData),
    307      mAsyncOpen(aIPCData.asyncOpen()),
    308      mRedirectStart(aIPCData.redirectStart()),
    309      mRedirectEnd(aIPCData.redirectEnd()),
    310      mDomainLookupStart(aIPCData.domainLookupStart()),
    311      mDomainLookupEnd(aIPCData.domainLookupEnd()),
    312      mConnectStart(aIPCData.connectStart()),
    313      mSecureConnectionStart(aIPCData.secureConnectionStart()),
    314      mConnectEnd(aIPCData.connectEnd()),
    315      mRequestStart(aIPCData.requestStart()),
    316      mResponseStart(aIPCData.responseStart()),
    317      mCacheReadStart(aIPCData.cacheReadStart()),
    318      mResponseEnd(aIPCData.responseEnd()),
    319      mCacheReadEnd(aIPCData.cacheReadEnd()),
    320      mWorkerStart(aIPCData.workerStart()),
    321      mWorkerRequestStart(aIPCData.workerRequestStart()),
    322      mWorkerResponseEnd(aIPCData.workerResponseEnd()),
    323      mZeroTime(aIPCData.zeroTime()),
    324      mFetchStart(aIPCData.fetchStart()),
    325      mTransferSize(aIPCData.transferSize()),
    326      mRenderBlockingStatus(aIPCData.renderBlocking()
    327                                ? RenderBlockingStatusType::Blocking
    328                                : RenderBlockingStatusType::Non_blocking) {}
    329 
    330 IPCPerformanceTimingData PerformanceTimingData::ToIPC() {
    331  nsTArray<IPCServerTiming> ipcServerTiming;
    332  for (auto& serverTimingData : mServerTiming) {
    333    nsAutoCString name;
    334    (void)serverTimingData->GetName(name);
    335    double duration = 0;
    336    (void)serverTimingData->GetDuration(&duration);
    337    nsAutoCString description;
    338    (void)serverTimingData->GetDescription(description);
    339    ipcServerTiming.AppendElement(IPCServerTiming(name, duration, description));
    340  }
    341  bool renderBlocking =
    342      mRenderBlockingStatus == RenderBlockingStatusType::Blocking;
    343  return IPCPerformanceTimingData(
    344      ipcServerTiming, mNextHopProtocol, mAsyncOpen, mRedirectStart,
    345      mRedirectEnd, mDomainLookupStart, mDomainLookupEnd, mConnectStart,
    346      mSecureConnectionStart, mConnectEnd, mRequestStart, mResponseStart,
    347      mCacheReadStart, mResponseEnd, mCacheReadEnd, mWorkerStart,
    348      mWorkerRequestStart, mWorkerResponseEnd, mZeroTime, mFetchStart,
    349      mEncodedBodySize, mTransferSize, mDecodedBodySize, mResponseStatus,
    350      mRedirectCount, renderBlocking, mContentType, mAllRedirectsSameOrigin,
    351      mAllRedirectsPassTAO, mSecureConnection, mBodyInfoAccessAllowed,
    352      mTimingAllowed, mInitialized);
    353 }
    354 
    355 void CacheablePerformanceTimingData::SetCacheablePropertiesFromHttpChannel(
    356    nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel) {
    357  MOZ_ASSERT(aHttpChannel);
    358 
    359  nsAutoCString protocol;
    360  (void)aHttpChannel->GetProtocolVersion(protocol);
    361  CopyUTF8toUTF16(protocol, mNextHopProtocol);
    362 
    363  (void)aHttpChannel->GetEncodedBodySize(&mEncodedBodySize);
    364  (void)aHttpChannel->GetDecodedBodySize(&mDecodedBodySize);
    365  if (mDecodedBodySize == 0) {
    366    mDecodedBodySize = mEncodedBodySize;
    367  }
    368 
    369  uint32_t responseStatus = 0;
    370  (void)aHttpChannel->GetResponseStatus(&responseStatus);
    371  mResponseStatus = static_cast<uint16_t>(responseStatus);
    372 
    373  nsAutoCString contentType;
    374  (void)aHttpChannel->GetContentType(contentType);
    375  CopyUTF8toUTF16(contentType, mContentType);
    376 
    377  mBodyInfoAccessAllowed =
    378      CheckBodyInfoAccessAllowedForOrigin(aHttpChannel, aChannel);
    379  mTimingAllowed = CheckTimingAllowedForOrigin(aHttpChannel, aChannel);
    380  aChannel->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO);
    381 
    382  aChannel->GetNativeServerTiming(mServerTiming);
    383 }
    384 
    385 void PerformanceTimingData::SetPropertiesFromHttpChannel(
    386    nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel) {
    387  SetCacheablePropertiesFromHttpChannel(aHttpChannel, aChannel);
    388  SetTransferSizeFromHttpChannel(aHttpChannel);
    389 }
    390 
    391 void PerformanceTimingData::SetTransferSizeFromHttpChannel(
    392    nsIHttpChannel* aHttpChannel) {
    393  (void)aHttpChannel->GetTransferSize(&mTransferSize);
    394 }
    395 
    396 PerformanceTiming::~PerformanceTiming() = default;
    397 
    398 DOMHighResTimeStamp PerformanceTimingData::FetchStartHighRes(
    399    Performance* aPerformance) {
    400  MOZ_ASSERT(aPerformance);
    401 
    402  if (!mFetchStart) {
    403    if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    404      return mZeroTime;
    405    }
    406    MOZ_ASSERT(!mAsyncOpen.IsNull(),
    407               "The fetch start time stamp should always be "
    408               "valid if the performance timing is enabled");
    409    if (!mAsyncOpen.IsNull()) {
    410      if (!mWorkerRequestStart.IsNull() && mWorkerRequestStart > mAsyncOpen) {
    411        mFetchStart = TimeStampToDOMHighRes(aPerformance, mWorkerRequestStart);
    412      } else {
    413        mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
    414      }
    415    }
    416  }
    417  return nsRFPService::ReduceTimePrecisionAsMSecs(
    418      mFetchStart, aPerformance->GetRandomTimelineSeed(),
    419      aPerformance->GetRTPCallerType());
    420 }
    421 
    422 DOMTimeMilliSec PerformanceTiming::FetchStart() {
    423  return static_cast<int64_t>(mTimingData->FetchStartHighRes(mPerformance));
    424 }
    425 
    426 nsITimedChannel::BodyInfoAccess
    427 CacheablePerformanceTimingData::CheckBodyInfoAccessAllowedForOrigin(
    428    nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel) {
    429  // Check if the resource is either same origin as the page that started
    430  // the load, or if the response contains an Access-Control-Allow-Origin
    431  // header with the domain of the page that started the load.
    432  MOZ_ASSERT(aChannel);
    433 
    434  if (!IsInitialized()) {
    435    return nsITimedChannel::BodyInfoAccess::DISALLOWED;
    436  }
    437 
    438  // Check that the current document passes the check.
    439  nsCOMPtr<nsILoadInfo> loadInfo = aResourceChannel->LoadInfo();
    440 
    441  // TYPE_DOCUMENT loads have no loadingPrincipal.
    442  if (loadInfo->GetExternalContentPolicyType() ==
    443      ExtContentPolicy::TYPE_DOCUMENT) {
    444    return nsITimedChannel::BodyInfoAccess::ALLOW_ALL;
    445  }
    446 
    447  nsCOMPtr<nsIPrincipal> principal = loadInfo->GetLoadingPrincipal();
    448  if (!principal) {
    449    return nsITimedChannel::BodyInfoAccess::DISALLOWED;
    450  }
    451  return aChannel->BodyInfoAccessAllowedCheck(principal);
    452 }
    453 
    454 bool CacheablePerformanceTimingData::CheckTimingAllowedForOrigin(
    455    nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel) {
    456  // Check if the resource is either same origin as the page that started
    457  // the load, or if the response contains the proper Timing-Allow-Origin
    458  // header with the domain of the page that started the load.
    459  MOZ_ASSERT(aChannel);
    460 
    461  if (!IsInitialized()) {
    462    return false;
    463  }
    464 
    465  // Check that the current document passes the check.
    466  nsCOMPtr<nsILoadInfo> loadInfo = aResourceChannel->LoadInfo();
    467 
    468  // TYPE_DOCUMENT loads have no loadingPrincipal.
    469  if (loadInfo->GetExternalContentPolicyType() ==
    470      ExtContentPolicy::TYPE_DOCUMENT) {
    471    return true;
    472  }
    473 
    474  nsCOMPtr<nsIPrincipal> principal = loadInfo->GetLoadingPrincipal();
    475  return principal && aChannel->TimingAllowCheck(principal);
    476 }
    477 
    478 uint8_t CacheablePerformanceTimingData::GetRedirectCount() const {
    479  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    480    return 0;
    481  }
    482  if (!mAllRedirectsSameOrigin) {
    483    return 0;
    484  }
    485  return mRedirectCount;
    486 }
    487 
    488 bool PerformanceTimingData::ShouldReportCrossOriginRedirect(
    489    bool aEnsureSameOriginAndIgnoreTAO) const {
    490  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    491    return false;
    492  }
    493 
    494  if (!mTimingAllowed || mRedirectCount == 0) {
    495    return false;
    496  }
    497 
    498  // If the redirect count is 0, or if one of the cross-origin
    499  // redirects doesn't have the proper Timing-Allow-Origin header,
    500  // then RedirectStart and RedirectEnd will be set to zero
    501  return aEnsureSameOriginAndIgnoreTAO ? mAllRedirectsSameOrigin
    502                                       : mAllRedirectsPassTAO;
    503 }
    504 
    505 DOMHighResTimeStamp PerformanceTimingData::AsyncOpenHighRes(
    506    Performance* aPerformance) {
    507  MOZ_ASSERT(aPerformance);
    508 
    509  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
    510      mAsyncOpen.IsNull()) {
    511    return mZeroTime;
    512  }
    513  DOMHighResTimeStamp rawValue =
    514      TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
    515  return nsRFPService::ReduceTimePrecisionAsMSecs(
    516      rawValue, aPerformance->GetRandomTimelineSeed(),
    517      aPerformance->GetRTPCallerType());
    518 }
    519 
    520 DOMHighResTimeStamp PerformanceTimingData::WorkerStartHighRes(
    521    Performance* aPerformance) {
    522  MOZ_ASSERT(aPerformance);
    523 
    524  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
    525      mWorkerStart.IsNull()) {
    526    return mZeroTime;
    527  }
    528  DOMHighResTimeStamp rawValue =
    529      TimeStampToDOMHighRes(aPerformance, mWorkerStart);
    530  return nsRFPService::ReduceTimePrecisionAsMSecs(
    531      rawValue, aPerformance->GetRandomTimelineSeed(),
    532      aPerformance->GetRTPCallerType());
    533 }
    534 
    535 /**
    536 * RedirectStartHighRes() is used by both the navigation timing and the
    537 * resource timing. Since, navigation timing and resource timing check and
    538 * interpret cross-domain redirects in a different manner,
    539 * RedirectStartHighRes() will make no checks for cross-domain redirect.
    540 * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
    541 * and PerformanceResourceTiming::RedirectStart() to make such verifications.
    542 *
    543 * @return a valid timing if the Performance Timing is enabled
    544 */
    545 DOMHighResTimeStamp PerformanceTimingData::RedirectStartHighRes(
    546    Performance* aPerformance) {
    547  MOZ_ASSERT(aPerformance);
    548 
    549  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    550    return mZeroTime;
    551  }
    552  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectStart);
    553 }
    554 
    555 DOMTimeMilliSec PerformanceTiming::RedirectStart() {
    556  if (!mTimingData->IsInitialized()) {
    557    return 0;
    558  }
    559  // We have to check if all the redirect URIs had the same origin (since there
    560  // is no check in RedirectStartHighRes())
    561  if (mTimingData->AllRedirectsSameOrigin() &&
    562      mTimingData->RedirectCountReal()) {
    563    return static_cast<int64_t>(
    564        mTimingData->RedirectStartHighRes(mPerformance));
    565  }
    566  return 0;
    567 }
    568 
    569 /**
    570 * RedirectEndHighRes() is used by both the navigation timing and the resource
    571 * timing. Since, navigation timing and resource timing check and interpret
    572 * cross-domain redirects in a different manner, RedirectEndHighRes() will make
    573 * no checks for cross-domain redirect. It's up to the consumers of this method
    574 * (PerformanceTiming::RedirectEnd() and
    575 * PerformanceResourceTiming::RedirectEnd() to make such verifications.
    576 *
    577 * @return a valid timing if the Performance Timing is enabled
    578 */
    579 DOMHighResTimeStamp PerformanceTimingData::RedirectEndHighRes(
    580    Performance* aPerformance) {
    581  MOZ_ASSERT(aPerformance);
    582 
    583  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    584    return mZeroTime;
    585  }
    586  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectEnd);
    587 }
    588 
    589 DOMTimeMilliSec PerformanceTiming::RedirectEnd() {
    590  if (!mTimingData->IsInitialized()) {
    591    return 0;
    592  }
    593  // We have to check if all the redirect URIs had the same origin (since there
    594  // is no check in RedirectEndHighRes())
    595  if (mTimingData->AllRedirectsSameOrigin() &&
    596      mTimingData->RedirectCountReal()) {
    597    return static_cast<int64_t>(mTimingData->RedirectEndHighRes(mPerformance));
    598  }
    599  return 0;
    600 }
    601 
    602 DOMHighResTimeStamp PerformanceTimingData::DomainLookupStartHighRes(
    603    Performance* aPerformance) {
    604  MOZ_ASSERT(aPerformance);
    605 
    606  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    607    return mZeroTime;
    608  }
    609  // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
    610  if (aPerformance->ShouldResistFingerprinting()) {
    611    return FetchStartHighRes(aPerformance);
    612  }
    613  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance,
    614                                                  mDomainLookupStart);
    615 }
    616 
    617 DOMTimeMilliSec PerformanceTiming::DomainLookupStart() {
    618  return static_cast<int64_t>(
    619      mTimingData->DomainLookupStartHighRes(mPerformance));
    620 }
    621 
    622 DOMHighResTimeStamp PerformanceTimingData::DomainLookupEndHighRes(
    623    Performance* aPerformance) {
    624  MOZ_ASSERT(aPerformance);
    625 
    626  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    627    return mZeroTime;
    628  }
    629  // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
    630  if (aPerformance->ShouldResistFingerprinting()) {
    631    return FetchStartHighRes(aPerformance);
    632  }
    633  // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
    634  if (mDomainLookupEnd.IsNull()) {
    635    return DomainLookupStartHighRes(aPerformance);
    636  }
    637  DOMHighResTimeStamp rawValue =
    638      TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd);
    639  return nsRFPService::ReduceTimePrecisionAsMSecs(
    640      rawValue, aPerformance->GetRandomTimelineSeed(),
    641      aPerformance->GetRTPCallerType());
    642 }
    643 
    644 DOMTimeMilliSec PerformanceTiming::DomainLookupEnd() {
    645  return static_cast<int64_t>(
    646      mTimingData->DomainLookupEndHighRes(mPerformance));
    647 }
    648 
    649 DOMHighResTimeStamp PerformanceTimingData::ConnectStartHighRes(
    650    Performance* aPerformance) {
    651  MOZ_ASSERT(aPerformance);
    652 
    653  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    654    return mZeroTime;
    655  }
    656  if (mConnectStart.IsNull()) {
    657    return DomainLookupEndHighRes(aPerformance);
    658  }
    659  DOMHighResTimeStamp rawValue =
    660      TimeStampToDOMHighRes(aPerformance, mConnectStart);
    661  return nsRFPService::ReduceTimePrecisionAsMSecs(
    662      rawValue, aPerformance->GetRandomTimelineSeed(),
    663      aPerformance->GetRTPCallerType());
    664 }
    665 
    666 DOMTimeMilliSec PerformanceTiming::ConnectStart() {
    667  return static_cast<int64_t>(mTimingData->ConnectStartHighRes(mPerformance));
    668 }
    669 
    670 DOMHighResTimeStamp PerformanceTimingData::SecureConnectionStartHighRes(
    671    Performance* aPerformance) {
    672  MOZ_ASSERT(aPerformance);
    673 
    674  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    675    return mZeroTime;
    676  }
    677  if (!mSecureConnection) {
    678    return 0;  // We use 0 here, because mZeroTime is sometimes set to the
    679               // navigation start time.
    680  }
    681  if (mSecureConnectionStart.IsNull()) {
    682    return ConnectStartHighRes(aPerformance);
    683  }
    684  DOMHighResTimeStamp rawValue =
    685      TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart);
    686  return nsRFPService::ReduceTimePrecisionAsMSecs(
    687      rawValue, aPerformance->GetRandomTimelineSeed(),
    688      aPerformance->GetRTPCallerType());
    689 }
    690 
    691 DOMTimeMilliSec PerformanceTiming::SecureConnectionStart() {
    692  return static_cast<int64_t>(
    693      mTimingData->SecureConnectionStartHighRes(mPerformance));
    694 }
    695 
    696 DOMHighResTimeStamp PerformanceTimingData::ConnectEndHighRes(
    697    Performance* aPerformance) {
    698  MOZ_ASSERT(aPerformance);
    699 
    700  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    701    return mZeroTime;
    702  }
    703  // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
    704  if (mConnectEnd.IsNull()) {
    705    return ConnectStartHighRes(aPerformance);
    706  }
    707  DOMHighResTimeStamp rawValue =
    708      TimeStampToDOMHighRes(aPerformance, mConnectEnd);
    709  return nsRFPService::ReduceTimePrecisionAsMSecs(
    710      rawValue, aPerformance->GetRandomTimelineSeed(),
    711      aPerformance->GetRTPCallerType());
    712 }
    713 
    714 DOMTimeMilliSec PerformanceTiming::ConnectEnd() {
    715  return static_cast<int64_t>(mTimingData->ConnectEndHighRes(mPerformance));
    716 }
    717 
    718 DOMHighResTimeStamp PerformanceTimingData::RequestStartHighRes(
    719    Performance* aPerformance) {
    720  MOZ_ASSERT(aPerformance);
    721 
    722  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    723    return mZeroTime;
    724  }
    725 
    726  if (mRequestStart.IsNull()) {
    727    mRequestStart = mWorkerRequestStart;
    728  }
    729 
    730  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRequestStart);
    731 }
    732 
    733 DOMTimeMilliSec PerformanceTiming::RequestStart() {
    734  return static_cast<int64_t>(mTimingData->RequestStartHighRes(mPerformance));
    735 }
    736 
    737 DOMHighResTimeStamp PerformanceTimingData::ResponseStartHighRes(
    738    Performance* aPerformance) {
    739  MOZ_ASSERT(aPerformance);
    740 
    741  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    742    return mZeroTime;
    743  }
    744  if (mResponseStart.IsNull() ||
    745      (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) {
    746    mResponseStart = mCacheReadStart;
    747  }
    748 
    749  if (mResponseStart.IsNull() ||
    750      (!mRequestStart.IsNull() && mResponseStart < mRequestStart)) {
    751    mResponseStart = mRequestStart;
    752  }
    753  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mResponseStart);
    754 }
    755 
    756 DOMTimeMilliSec PerformanceTiming::ResponseStart() {
    757  return static_cast<int64_t>(mTimingData->ResponseStartHighRes(mPerformance));
    758 }
    759 
    760 DOMHighResTimeStamp PerformanceTimingData::ResponseEndHighRes(
    761    Performance* aPerformance) {
    762  MOZ_ASSERT(aPerformance);
    763 
    764  if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
    765    return mZeroTime;
    766  }
    767  if (mResponseEnd.IsNull() ||
    768      (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
    769    mResponseEnd = mCacheReadEnd;
    770  }
    771  if (mResponseEnd.IsNull()) {
    772    mResponseEnd = mWorkerResponseEnd;
    773  }
    774  // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
    775  if (mResponseEnd.IsNull()) {
    776    return ResponseStartHighRes(aPerformance);
    777  }
    778  DOMHighResTimeStamp rawValue =
    779      TimeStampToDOMHighRes(aPerformance, mResponseEnd);
    780  return nsRFPService::ReduceTimePrecisionAsMSecs(
    781      rawValue, aPerformance->GetRandomTimelineSeed(),
    782      aPerformance->GetRTPCallerType());
    783 }
    784 
    785 DOMTimeMilliSec PerformanceTiming::ResponseEnd() {
    786  return static_cast<int64_t>(mTimingData->ResponseEndHighRes(mPerformance));
    787 }
    788 
    789 JSObject* PerformanceTiming::WrapObject(JSContext* cx,
    790                                        JS::Handle<JSObject*> aGivenProto) {
    791  return PerformanceTiming_Binding::Wrap(cx, this, aGivenProto);
    792 }
    793 
    794 bool PerformanceTiming::IsTopLevelContentDocument() const {
    795  nsCOMPtr<Document> document = mPerformance->GetDocumentIfCurrent();
    796  if (!document) {
    797    return false;
    798  }
    799 
    800  if (BrowsingContext* bc = document->GetBrowsingContext()) {
    801    return bc->IsTopContent();
    802  }
    803  return false;
    804 }
    805 
    806 nsTArray<nsCOMPtr<nsIServerTiming>>
    807 CacheablePerformanceTimingData::GetServerTiming() {
    808  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
    809      !TimingAllowed()) {
    810    return nsTArray<nsCOMPtr<nsIServerTiming>>();
    811  }
    812 
    813  return mServerTiming.Clone();
    814 }
    815 
    816 }  // namespace mozilla::dom