dom/performance/PerformanceTiming.cpp
author Jan de Mooij <jdemooij@mozilla.com>
Thu, 15 Aug 2019 16:13:57 +0000
changeset 488278 8bf0e43c0df043e3527a419e095e641ff52bfcb4
parent 485908 6d27ca6d890f0195e7b01271243416d07824b182
permissions -rw-r--r--
Bug 1505689 part 6 - Merge TraceJitScripts into JitScript::trace. r=tcampbell This also removes BaselineScript::Trace and IonScript::Trace that were just forwarding to the non-static trace(). Differential Revision: https://phabricator.services.mozilla.com/D41708

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

#include "PerformanceTiming.h"
#include "mozilla/dom/PerformanceTimingBinding.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/Telemetry.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "mozilla/dom/Document.h"
#include "nsITimedChannel.h"

namespace mozilla {
namespace dom {

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance)

NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)

/* static */
PerformanceTimingData* PerformanceTimingData::Create(
    nsITimedChannel* aTimedChannel, nsIHttpChannel* aChannel,
    DOMHighResTimeStamp aZeroTime, nsAString& aInitiatorType,
    nsAString& aEntryName) {
  MOZ_ASSERT(NS_IsMainThread());

  // Check if resource timing is prefed off.
  if (!StaticPrefs::dom_enable_resource_timing()) {
    return nullptr;
  }

  if (!aChannel || !aTimedChannel) {
    return nullptr;
  }

  bool reportTiming = true;
  aTimedChannel->GetReportResourceTiming(&reportTiming);

  if (!reportTiming) {
    return nullptr;
  }

  aTimedChannel->GetInitiatorType(aInitiatorType);

  // If the initiator type had no valid value, then set it to the default
  // ("other") value.
  if (aInitiatorType.IsEmpty()) {
    aInitiatorType = NS_LITERAL_STRING("other");
  }

  // According to the spec, "The name attribute must return the resolved URL
  // of the requested resource. This attribute must not change even if the
  // fetch redirected to a different URL."
  nsCOMPtr<nsIURI> originalURI;
  aChannel->GetOriginalURI(getter_AddRefs(originalURI));

  nsAutoCString name;
  originalURI->GetSpec(name);
  aEntryName = NS_ConvertUTF8toUTF16(name);

  // 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).
  return new PerformanceTimingData(aTimedChannel, aChannel, 0);
}

PerformanceTiming::PerformanceTiming(Performance* aPerformance,
                                     nsITimedChannel* aChannel,
                                     nsIHttpChannel* aHttpChannel,
                                     DOMHighResTimeStamp aZeroTime)
    : mPerformance(aPerformance) {
  MOZ_ASSERT(aPerformance, "Parent performance object should be provided");

  mTimingData.reset(new PerformanceTimingData(
      aChannel, aHttpChannel,
      aPerformance->IsSystemPrincipal()
          ? aZeroTime
          : nsRFPService::ReduceTimePrecisionAsMSecs(
                aZeroTime, aPerformance->GetRandomTimelineSeed())));

  // Non-null aHttpChannel implies that this PerformanceTiming object is being
  // used for subresources, which is irrelevant to this probe.
  if (!aHttpChannel && StaticPrefs::dom_enable_performance() &&
      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 = aZeroTime;

  if (!StaticPrefs::dom_enable_performance() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    mZeroTime = 0;
  }

  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) {
    mSecureConnection = uri->SchemeIs("https");
  }

  if (aChannel) {
    aChannel->GetAsyncOpen(&mAsyncOpen);
    aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
    aChannel->GetRedirectCount(&mRedirectCount);
    aChannel->GetRedirectStart(&mRedirectStart);
    aChannel->GetRedirectEnd(&mRedirectEnd);
    aChannel->GetDomainLookupStart(&mDomainLookupStart);
    aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
    aChannel->GetConnectStart(&mConnectStart);
    aChannel->GetSecureConnectionStart(&mSecureConnectionStart);
    aChannel->GetConnectEnd(&mConnectEnd);
    aChannel->GetRequestStart(&mRequestStart);
    aChannel->GetResponseStart(&mResponseStart);
    aChannel->GetCacheReadStart(&mCacheReadStart);
    aChannel->GetResponseEnd(&mResponseEnd);
    aChannel->GetCacheReadEnd(&mCacheReadEnd);

    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.
    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;
      }

      if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
        mDomainLookupStart = *clampTime;
      }

      if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
        mDomainLookupEnd = *clampTime;
      }

      if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
        mConnectStart = *clampTime;
      }

      if (mSecureConnection && !mSecureConnectionStart.IsNull() &&
          mSecureConnectionStart < *clampTime) {
        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) {
    SetPropertiesFromHttpChannel(aHttpChannel, aChannel);
  }
}

void PerformanceTimingData::SetPropertiesFromHttpChannel(
    nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel) {
  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;
  }

  mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel);
  bool redirectsPassCheck = false;
  aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
  mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;

  aChannel->GetNativeServerTiming(mServerTiming);
}

PerformanceTiming::~PerformanceTiming() {}

DOMHighResTimeStamp PerformanceTimingData::FetchStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!mFetchStart) {
    if (!StaticPrefs::dom_enable_performance() || !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(aPerformance, mWorkerRequestStart);
      } else {
        mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
      }
    }
  }
  if (aPerformance->IsSystemPrincipal()) {
    return mFetchStart;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      mFetchStart, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::FetchStart() {
  return static_cast<int64_t>(mTimingData->FetchStartHighRes(mPerformance));
}

bool PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel,
                                               nsITimedChannel* aChannel) {
  if (!IsInitialized()) {
    return false;
  }

  // Check that the current document passes the ckeck.
  nsCOMPtr<nsILoadInfo> loadInfo = aResourceChannel->LoadInfo();

  // TYPE_DOCUMENT loads have no loadingPrincipal.
  if (loadInfo->GetExternalContentPolicyType() ==
      nsIContentPolicy::TYPE_DOCUMENT) {
    return true;
  }

  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);
}

uint8_t PerformanceTimingData::GetRedirectCount() const {
  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return 0;
  }
  if (!mAllRedirectsSameOrigin) {
    return 0;
  }
  return mRedirectCount;
}

bool PerformanceTimingData::ShouldReportCrossOriginRedirect() const {
  if (!StaticPrefs::dom_enable_performance() || !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 PerformanceTimingData::AsyncOpenHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) {
    return mZeroTime;
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mAsyncOpen);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMHighResTimeStamp PerformanceTimingData::WorkerStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) {
    return mZeroTime;
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mWorkerStart);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

/**
 * RedirectStartHighRes() is used by both the navigation timing and the
 * resource timing. Since, navigation timing and resource timing check and
 * interpret cross-domain redirects in a different manner,
 * RedirectStartHighRes() will make no checks for cross-domain redirect.
 * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
 * and PerformanceResourceTiming::RedirectStart() to make such verifications.
 *
 * @return a valid timing if the Performance Timing is enabled
 */
DOMHighResTimeStamp PerformanceTimingData::RedirectStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectStart);
}

DOMTimeMilliSec PerformanceTiming::RedirectStart() {
  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 (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 PerformanceTimingData::RedirectEndHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectEnd);
}

DOMTimeMilliSec PerformanceTiming::RedirectEnd() {
  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 (mTimingData->AllRedirectsSameOrigin() &&
      mTimingData->RedirectCountReal()) {
    return static_cast<int64_t>(mTimingData->RedirectEndHighRes(mPerformance));
  }
  return 0;
}

DOMHighResTimeStamp PerformanceTimingData::DomainLookupStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance,
                                                  mDomainLookupStart);
}

DOMTimeMilliSec PerformanceTiming::DomainLookupStart() {
  return static_cast<int64_t>(
      mTimingData->DomainLookupStartHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::DomainLookupEndHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
  if (mDomainLookupEnd.IsNull()) {
    return DomainLookupStartHighRes(aPerformance);
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::DomainLookupEnd() {
  return static_cast<int64_t>(
      mTimingData->DomainLookupEndHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::ConnectStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  if (mConnectStart.IsNull()) {
    return DomainLookupEndHighRes(aPerformance);
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mConnectStart);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::ConnectStart() {
  return static_cast<int64_t>(mTimingData->ConnectStartHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::SecureConnectionStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  if (!mSecureConnection) {
    return 0;  // We use 0 here, because mZeroTime is sometimes set to the
               // navigation start time.
  }
  if (mSecureConnectionStart.IsNull()) {
    return mZeroTime;
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::SecureConnectionStart() {
  return static_cast<int64_t>(
      mTimingData->SecureConnectionStartHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::ConnectEndHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }
  // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
  if (mConnectEnd.IsNull()) {
    return ConnectStartHighRes(aPerformance);
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mConnectEnd);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::ConnectEnd() {
  return static_cast<int64_t>(mTimingData->ConnectEndHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::RequestStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      nsContentUtils::ShouldResistFingerprinting()) {
    return mZeroTime;
  }

  if (mRequestStart.IsNull()) {
    mRequestStart = mWorkerRequestStart;
  }

  return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRequestStart);
}

DOMTimeMilliSec PerformanceTiming::RequestStart() {
  return static_cast<int64_t>(mTimingData->RequestStartHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::ResponseStartHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !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(aPerformance, mResponseStart);
}

DOMTimeMilliSec PerformanceTiming::ResponseStart() {
  return static_cast<int64_t>(mTimingData->ResponseStartHighRes(mPerformance));
}

DOMHighResTimeStamp PerformanceTimingData::ResponseEndHighRes(
    Performance* aPerformance) {
  MOZ_ASSERT(aPerformance);

  if (!StaticPrefs::dom_enable_performance() || !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
  if (mResponseEnd.IsNull()) {
    return ResponseStartHighRes(aPerformance);
  }
  DOMHighResTimeStamp rawValue =
      TimeStampToDOMHighRes(aPerformance, mResponseEnd);
  if (aPerformance->IsSystemPrincipal()) {
    return rawValue;
  }
  return nsRFPService::ReduceTimePrecisionAsMSecs(
      rawValue, aPerformance->GetRandomTimelineSeed());
}

DOMTimeMilliSec PerformanceTiming::ResponseEnd() {
  return static_cast<int64_t>(mTimingData->ResponseEndHighRes(mPerformance));
}

JSObject* PerformanceTiming::WrapObject(JSContext* cx,
                                        JS::Handle<JSObject*> aGivenProto) {
  return PerformanceTiming_Binding::Wrap(cx, this, aGivenProto);
}

bool PerformanceTiming::IsTopLevelContentDocument() const {
  nsCOMPtr<Document> document = mPerformance->GetDocumentIfCurrent();
  if (!document) {
    return false;
  }
  nsCOMPtr<nsIDocShell> docShell = document->GetDocShell();
  if (!docShell) {
    return false;
  }
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
  Unused << docShell->GetInProcessSameTypeRootTreeItem(
      getter_AddRefs(rootItem));
  if (rootItem.get() != static_cast<nsIDocShellTreeItem*>(docShell.get())) {
    return false;
  }
  return rootItem->ItemType() == nsIDocShellTreeItem::typeContent;
}

nsTArray<nsCOMPtr<nsIServerTiming>> PerformanceTimingData::GetServerTiming() {
  if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
      !TimingAllowed() || nsContentUtils::ShouldResistFingerprinting()) {
    return nsTArray<nsCOMPtr<nsIServerTiming>>();
  }

  return nsTArray<nsCOMPtr<nsIServerTiming>>(mServerTiming);
}

}  // namespace dom
}  // namespace mozilla