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/. */
7 #include "PerformanceTiming.h"
8 #include "mozilla/BasePrincipal.h"
9 #include "mozilla/dom/PerformanceTimingBinding.h"
10 #include "mozilla/StaticPrefs_dom.h"
11 #include "mozilla/Telemetry.h"
12 #include "nsIDocShell.h"
13 #include "nsIDocShellTreeItem.h"
14 #include "nsIHttpChannel.h"
15 #include "mozilla/dom/BrowsingContext.h"
16 #include "mozilla/dom/Document.h"
17 #include "nsITimedChannel.h"
19 namespace mozilla::dom
{
21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming
, mPerformance
)
23 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming
, AddRef
)
24 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming
, Release
)
27 PerformanceTimingData
* PerformanceTimingData::Create(
28 nsITimedChannel
* aTimedChannel
, nsIHttpChannel
* aChannel
,
29 DOMHighResTimeStamp aZeroTime
, nsAString
& aInitiatorType
,
30 nsAString
& aEntryName
) {
31 MOZ_ASSERT(NS_IsMainThread());
33 // Check if resource timing is prefed off.
34 if (!StaticPrefs::dom_enable_resource_timing()) {
38 if (!aChannel
|| !aTimedChannel
) {
42 bool reportTiming
= true;
43 aTimedChannel
->GetReportResourceTiming(&reportTiming
);
49 aTimedChannel
->GetInitiatorType(aInitiatorType
);
51 // If the initiator type had no valid value, then set it to the default
53 if (aInitiatorType
.IsEmpty()) {
54 aInitiatorType
= u
"other"_ns
;
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
));
64 originalURI
->GetSpec(name
);
65 CopyUTF8toUTF16(name
, aEntryName
);
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);
76 PerformanceTiming::PerformanceTiming(Performance
* aPerformance
,
77 nsITimedChannel
* aChannel
,
78 nsIHttpChannel
* aHttpChannel
,
79 DOMHighResTimeStamp aZeroTime
)
80 : mPerformance(aPerformance
) {
81 MOZ_ASSERT(aPerformance
, "Parent performance object should be provided");
83 mTimingData
.reset(new PerformanceTimingData(
84 aChannel
, aHttpChannel
,
85 nsRFPService::ReduceTimePrecisionAsMSecs(
86 aZeroTime
, aPerformance
->GetRandomTimelineSeed(),
87 aPerformance
->IsSystemPrincipal(),
88 aPerformance
->CrossOriginIsolated())));
90 // Non-null aHttpChannel implies that this PerformanceTiming object is being
91 // used for subresources, which is irrelevant to this probe.
92 if (!aHttpChannel
&& StaticPrefs::dom_enable_performance() &&
93 IsTopLevelContentDocument()) {
94 Telemetry::Accumulate(Telemetry::TIME_TO_RESPONSE_START_MS
,
95 mTimingData
->ResponseStartHighRes(aPerformance
) -
96 mTimingData
->ZeroTime());
100 // Copy the timing info from the channel so we don't need to keep the channel
101 // alive just to get the timestamps.
102 PerformanceTimingData::PerformanceTimingData(nsITimedChannel
* aChannel
,
103 nsIHttpChannel
* aHttpChannel
,
104 DOMHighResTimeStamp aZeroTime
)
111 mAllRedirectsSameOrigin(true),
112 mAllRedirectsPassTAO(true),
113 mSecureConnection(false),
114 mTimingAllowed(true),
115 mInitialized(false) {
116 mInitialized
= !!aChannel
;
117 mZeroTime
= aZeroTime
;
119 if (!StaticPrefs::dom_enable_performance() ||
120 nsContentUtils::ShouldResistFingerprinting()) {
124 nsCOMPtr
<nsIURI
> uri
;
126 aHttpChannel
->GetURI(getter_AddRefs(uri
));
128 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
130 httpChannel
->GetURI(getter_AddRefs(uri
));
135 mSecureConnection
= uri
->SchemeIs("https");
139 aChannel
->GetAsyncOpen(&mAsyncOpen
);
140 aChannel
->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin
);
141 aChannel
->GetRedirectCount(&mRedirectCount
);
142 aChannel
->GetRedirectStart(&mRedirectStart
);
143 aChannel
->GetRedirectEnd(&mRedirectEnd
);
144 aChannel
->GetDomainLookupStart(&mDomainLookupStart
);
145 aChannel
->GetDomainLookupEnd(&mDomainLookupEnd
);
146 aChannel
->GetConnectStart(&mConnectStart
);
147 aChannel
->GetSecureConnectionStart(&mSecureConnectionStart
);
148 aChannel
->GetConnectEnd(&mConnectEnd
);
149 aChannel
->GetRequestStart(&mRequestStart
);
150 aChannel
->GetResponseStart(&mResponseStart
);
151 aChannel
->GetCacheReadStart(&mCacheReadStart
);
152 aChannel
->GetResponseEnd(&mResponseEnd
);
153 aChannel
->GetCacheReadEnd(&mCacheReadEnd
);
155 aChannel
->GetDispatchFetchEventStart(&mWorkerStart
);
156 aChannel
->GetHandleFetchEventStart(&mWorkerRequestStart
);
157 // TODO: Track when FetchEvent.respondWith() promise resolves as
158 // ServiceWorker interception responseStart?
159 aChannel
->GetHandleFetchEventEnd(&mWorkerResponseEnd
);
161 // The performance timing api essentially requires that the event timestamps
162 // have a strict relation with each other. The truth, however, is the
163 // browser engages in a number of speculative activities that sometimes mean
164 // connections and lookups begin at different times. Workaround that here by
165 // clamping these values to what we expect FetchStart to be. This means the
166 // later of AsyncOpen or WorkerStart times.
167 if (!mAsyncOpen
.IsNull()) {
168 // We want to clamp to the expected FetchStart value. This is later of
169 // the AsyncOpen and WorkerStart values.
170 const TimeStamp
* clampTime
= &mAsyncOpen
;
171 if (!mWorkerStart
.IsNull() && mWorkerStart
> mAsyncOpen
) {
172 clampTime
= &mWorkerStart
;
175 if (!mDomainLookupStart
.IsNull() && mDomainLookupStart
< *clampTime
) {
176 mDomainLookupStart
= *clampTime
;
179 if (!mDomainLookupEnd
.IsNull() && mDomainLookupEnd
< *clampTime
) {
180 mDomainLookupEnd
= *clampTime
;
183 if (!mConnectStart
.IsNull() && mConnectStart
< *clampTime
) {
184 mConnectStart
= *clampTime
;
187 if (mSecureConnection
&& !mSecureConnectionStart
.IsNull() &&
188 mSecureConnectionStart
< *clampTime
) {
189 mSecureConnectionStart
= *clampTime
;
192 if (!mConnectEnd
.IsNull() && mConnectEnd
< *clampTime
) {
193 mConnectEnd
= *clampTime
;
198 // The aHttpChannel argument is null if this PerformanceTiming object is
199 // being used for navigation timing (which is only relevant for documents).
200 // It has a non-null value if this PerformanceTiming object is being used
201 // for resource timing, which can include document loads, both toplevel and
202 // in subframes, and resources linked from a document.
204 SetPropertiesFromHttpChannel(aHttpChannel
, aChannel
);
208 void PerformanceTimingData::SetPropertiesFromHttpChannel(
209 nsIHttpChannel
* aHttpChannel
, nsITimedChannel
* aChannel
) {
210 MOZ_ASSERT(aHttpChannel
);
212 nsAutoCString protocol
;
213 Unused
<< aHttpChannel
->GetProtocolVersion(protocol
);
214 CopyUTF8toUTF16(protocol
, mNextHopProtocol
);
216 Unused
<< aHttpChannel
->GetEncodedBodySize(&mEncodedBodySize
);
217 Unused
<< aHttpChannel
->GetTransferSize(&mTransferSize
);
218 Unused
<< aHttpChannel
->GetDecodedBodySize(&mDecodedBodySize
);
219 if (mDecodedBodySize
== 0) {
220 mDecodedBodySize
= mEncodedBodySize
;
223 mTimingAllowed
= CheckAllowedOrigin(aHttpChannel
, aChannel
);
224 aChannel
->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO
);
226 aChannel
->GetNativeServerTiming(mServerTiming
);
229 PerformanceTiming::~PerformanceTiming() = default;
231 DOMHighResTimeStamp
PerformanceTimingData::FetchStartHighRes(
232 Performance
* aPerformance
) {
233 MOZ_ASSERT(aPerformance
);
236 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
239 MOZ_ASSERT(!mAsyncOpen
.IsNull(),
240 "The fetch start time stamp should always be "
241 "valid if the performance timing is enabled");
242 if (!mAsyncOpen
.IsNull()) {
243 if (!mWorkerRequestStart
.IsNull() && mWorkerRequestStart
> mAsyncOpen
) {
244 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mWorkerRequestStart
);
246 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
250 return nsRFPService::ReduceTimePrecisionAsMSecs(
251 mFetchStart
, aPerformance
->GetRandomTimelineSeed(),
252 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
255 DOMTimeMilliSec
PerformanceTiming::FetchStart() {
256 return static_cast<int64_t>(mTimingData
->FetchStartHighRes(mPerformance
));
259 bool PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel
* aResourceChannel
,
260 nsITimedChannel
* aChannel
) {
261 if (!IsInitialized()) {
265 // Check that the current document passes the ckeck.
266 nsCOMPtr
<nsILoadInfo
> loadInfo
= aResourceChannel
->LoadInfo();
268 // TYPE_DOCUMENT loads have no loadingPrincipal.
269 if (loadInfo
->GetExternalContentPolicyType() ==
270 ExtContentPolicy::TYPE_DOCUMENT
) {
274 nsCOMPtr
<nsIPrincipal
> principal
= loadInfo
->GetLoadingPrincipal();
276 // Check if the resource is either same origin as the page that started
277 // the load, or if the response contains the proper Timing-Allow-Origin
278 // header with the domain of the page that started the load.
279 return aChannel
->TimingAllowCheck(principal
);
282 uint8_t PerformanceTimingData::GetRedirectCount() const {
283 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
284 nsContentUtils::ShouldResistFingerprinting()) {
287 if (!mAllRedirectsSameOrigin
) {
290 return mRedirectCount
;
293 bool PerformanceTimingData::ShouldReportCrossOriginRedirect(
294 bool aEnsureSameOriginAndIgnoreTAO
) const {
295 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
296 nsContentUtils::ShouldResistFingerprinting()) {
300 if (!mTimingAllowed
|| mRedirectCount
== 0) {
304 // If the redirect count is 0, or if one of the cross-origin
305 // redirects doesn't have the proper Timing-Allow-Origin header,
306 // then RedirectStart and RedirectEnd will be set to zero
307 return aEnsureSameOriginAndIgnoreTAO
? mAllRedirectsSameOrigin
308 : mAllRedirectsPassTAO
;
311 DOMHighResTimeStamp
PerformanceTimingData::AsyncOpenHighRes(
312 Performance
* aPerformance
) {
313 MOZ_ASSERT(aPerformance
);
315 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
316 mAsyncOpen
.IsNull()) {
319 DOMHighResTimeStamp rawValue
=
320 TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
321 return nsRFPService::ReduceTimePrecisionAsMSecs(
322 rawValue
, aPerformance
->GetRandomTimelineSeed(),
323 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
326 DOMHighResTimeStamp
PerformanceTimingData::WorkerStartHighRes(
327 Performance
* aPerformance
) {
328 MOZ_ASSERT(aPerformance
);
330 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
331 mWorkerStart
.IsNull()) {
334 DOMHighResTimeStamp rawValue
=
335 TimeStampToDOMHighRes(aPerformance
, mWorkerStart
);
336 return nsRFPService::ReduceTimePrecisionAsMSecs(
337 rawValue
, aPerformance
->GetRandomTimelineSeed(),
338 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
342 * RedirectStartHighRes() is used by both the navigation timing and the
343 * resource timing. Since, navigation timing and resource timing check and
344 * interpret cross-domain redirects in a different manner,
345 * RedirectStartHighRes() will make no checks for cross-domain redirect.
346 * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
347 * and PerformanceResourceTiming::RedirectStart() to make such verifications.
349 * @return a valid timing if the Performance Timing is enabled
351 DOMHighResTimeStamp
PerformanceTimingData::RedirectStartHighRes(
352 Performance
* aPerformance
) {
353 MOZ_ASSERT(aPerformance
);
355 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
358 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectStart
);
361 DOMTimeMilliSec
PerformanceTiming::RedirectStart() {
362 if (!mTimingData
->IsInitialized()) {
365 // We have to check if all the redirect URIs had the same origin (since there
366 // is no check in RedirectStartHighRes())
367 if (mTimingData
->AllRedirectsSameOrigin() &&
368 mTimingData
->RedirectCountReal()) {
369 return static_cast<int64_t>(
370 mTimingData
->RedirectStartHighRes(mPerformance
));
376 * RedirectEndHighRes() is used by both the navigation timing and the resource
377 * timing. Since, navigation timing and resource timing check and interpret
378 * cross-domain redirects in a different manner, RedirectEndHighRes() will make
379 * no checks for cross-domain redirect. It's up to the consumers of this method
380 * (PerformanceTiming::RedirectEnd() and
381 * PerformanceResourceTiming::RedirectEnd() to make such verifications.
383 * @return a valid timing if the Performance Timing is enabled
385 DOMHighResTimeStamp
PerformanceTimingData::RedirectEndHighRes(
386 Performance
* aPerformance
) {
387 MOZ_ASSERT(aPerformance
);
389 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
392 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectEnd
);
395 DOMTimeMilliSec
PerformanceTiming::RedirectEnd() {
396 if (!mTimingData
->IsInitialized()) {
399 // We have to check if all the redirect URIs had the same origin (since there
400 // is no check in RedirectEndHighRes())
401 if (mTimingData
->AllRedirectsSameOrigin() &&
402 mTimingData
->RedirectCountReal()) {
403 return static_cast<int64_t>(mTimingData
->RedirectEndHighRes(mPerformance
));
408 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupStartHighRes(
409 Performance
* aPerformance
) {
410 MOZ_ASSERT(aPerformance
);
412 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
415 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
416 if (nsContentUtils::ShouldResistFingerprinting()) {
417 return FetchStartHighRes(aPerformance
);
419 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
,
423 DOMTimeMilliSec
PerformanceTiming::DomainLookupStart() {
424 return static_cast<int64_t>(
425 mTimingData
->DomainLookupStartHighRes(mPerformance
));
428 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupEndHighRes(
429 Performance
* aPerformance
) {
430 MOZ_ASSERT(aPerformance
);
432 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
435 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
436 if (nsContentUtils::ShouldResistFingerprinting()) {
437 return FetchStartHighRes(aPerformance
);
439 // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
440 if (mDomainLookupEnd
.IsNull()) {
441 return DomainLookupStartHighRes(aPerformance
);
443 DOMHighResTimeStamp rawValue
=
444 TimeStampToDOMHighRes(aPerformance
, mDomainLookupEnd
);
445 return nsRFPService::ReduceTimePrecisionAsMSecs(
446 rawValue
, aPerformance
->GetRandomTimelineSeed(),
447 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
450 DOMTimeMilliSec
PerformanceTiming::DomainLookupEnd() {
451 return static_cast<int64_t>(
452 mTimingData
->DomainLookupEndHighRes(mPerformance
));
455 DOMHighResTimeStamp
PerformanceTimingData::ConnectStartHighRes(
456 Performance
* aPerformance
) {
457 MOZ_ASSERT(aPerformance
);
459 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
462 if (mConnectStart
.IsNull()) {
463 return DomainLookupEndHighRes(aPerformance
);
465 DOMHighResTimeStamp rawValue
=
466 TimeStampToDOMHighRes(aPerformance
, mConnectStart
);
467 return nsRFPService::ReduceTimePrecisionAsMSecs(
468 rawValue
, aPerformance
->GetRandomTimelineSeed(),
469 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
472 DOMTimeMilliSec
PerformanceTiming::ConnectStart() {
473 return static_cast<int64_t>(mTimingData
->ConnectStartHighRes(mPerformance
));
476 DOMHighResTimeStamp
PerformanceTimingData::SecureConnectionStartHighRes(
477 Performance
* aPerformance
) {
478 MOZ_ASSERT(aPerformance
);
480 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
483 if (!mSecureConnection
) {
484 return 0; // We use 0 here, because mZeroTime is sometimes set to the
485 // navigation start time.
487 if (mSecureConnectionStart
.IsNull()) {
488 return ConnectStartHighRes(aPerformance
);
490 DOMHighResTimeStamp rawValue
=
491 TimeStampToDOMHighRes(aPerformance
, mSecureConnectionStart
);
492 return nsRFPService::ReduceTimePrecisionAsMSecs(
493 rawValue
, aPerformance
->GetRandomTimelineSeed(),
494 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
497 DOMTimeMilliSec
PerformanceTiming::SecureConnectionStart() {
498 return static_cast<int64_t>(
499 mTimingData
->SecureConnectionStartHighRes(mPerformance
));
502 DOMHighResTimeStamp
PerformanceTimingData::ConnectEndHighRes(
503 Performance
* aPerformance
) {
504 MOZ_ASSERT(aPerformance
);
506 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
509 // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
510 if (mConnectEnd
.IsNull()) {
511 return ConnectStartHighRes(aPerformance
);
513 DOMHighResTimeStamp rawValue
=
514 TimeStampToDOMHighRes(aPerformance
, mConnectEnd
);
515 return nsRFPService::ReduceTimePrecisionAsMSecs(
516 rawValue
, aPerformance
->GetRandomTimelineSeed(),
517 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
520 DOMTimeMilliSec
PerformanceTiming::ConnectEnd() {
521 return static_cast<int64_t>(mTimingData
->ConnectEndHighRes(mPerformance
));
524 DOMHighResTimeStamp
PerformanceTimingData::RequestStartHighRes(
525 Performance
* aPerformance
) {
526 MOZ_ASSERT(aPerformance
);
528 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
532 if (mRequestStart
.IsNull()) {
533 mRequestStart
= mWorkerRequestStart
;
536 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRequestStart
);
539 DOMTimeMilliSec
PerformanceTiming::RequestStart() {
540 return static_cast<int64_t>(mTimingData
->RequestStartHighRes(mPerformance
));
543 DOMHighResTimeStamp
PerformanceTimingData::ResponseStartHighRes(
544 Performance
* aPerformance
) {
545 MOZ_ASSERT(aPerformance
);
547 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
550 if (mResponseStart
.IsNull() ||
551 (!mCacheReadStart
.IsNull() && mCacheReadStart
< mResponseStart
)) {
552 mResponseStart
= mCacheReadStart
;
555 if (mResponseStart
.IsNull() ||
556 (!mRequestStart
.IsNull() && mResponseStart
< mRequestStart
)) {
557 mResponseStart
= mRequestStart
;
559 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mResponseStart
);
562 DOMTimeMilliSec
PerformanceTiming::ResponseStart() {
563 return static_cast<int64_t>(mTimingData
->ResponseStartHighRes(mPerformance
));
566 DOMHighResTimeStamp
PerformanceTimingData::ResponseEndHighRes(
567 Performance
* aPerformance
) {
568 MOZ_ASSERT(aPerformance
);
570 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
573 if (mResponseEnd
.IsNull() ||
574 (!mCacheReadEnd
.IsNull() && mCacheReadEnd
< mResponseEnd
)) {
575 mResponseEnd
= mCacheReadEnd
;
577 if (mResponseEnd
.IsNull()) {
578 mResponseEnd
= mWorkerResponseEnd
;
580 // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
581 if (mResponseEnd
.IsNull()) {
582 return ResponseStartHighRes(aPerformance
);
584 DOMHighResTimeStamp rawValue
=
585 TimeStampToDOMHighRes(aPerformance
, mResponseEnd
);
586 return nsRFPService::ReduceTimePrecisionAsMSecs(
587 rawValue
, aPerformance
->GetRandomTimelineSeed(),
588 aPerformance
->IsSystemPrincipal(), aPerformance
->CrossOriginIsolated());
591 DOMTimeMilliSec
PerformanceTiming::ResponseEnd() {
592 return static_cast<int64_t>(mTimingData
->ResponseEndHighRes(mPerformance
));
595 JSObject
* PerformanceTiming::WrapObject(JSContext
* cx
,
596 JS::Handle
<JSObject
*> aGivenProto
) {
597 return PerformanceTiming_Binding::Wrap(cx
, this, aGivenProto
);
600 bool PerformanceTiming::IsTopLevelContentDocument() const {
601 nsCOMPtr
<Document
> document
= mPerformance
->GetDocumentIfCurrent();
606 if (BrowsingContext
* bc
= document
->GetBrowsingContext()) {
607 return bc
->IsTopContent();
612 nsTArray
<nsCOMPtr
<nsIServerTiming
>> PerformanceTimingData::GetServerTiming() {
613 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
615 return nsTArray
<nsCOMPtr
<nsIServerTiming
>>();
618 return mServerTiming
.Clone();
621 } // namespace mozilla::dom