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/StaticPrefs_dom.h"
10 #include "mozilla/dom/PerformanceTimingBinding.h"
11 #include "mozilla/glean/GleanMetrics.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
)
24 PerformanceTimingData
* PerformanceTimingData::Create(
25 nsITimedChannel
* aTimedChannel
, nsIHttpChannel
* aChannel
,
26 DOMHighResTimeStamp aZeroTime
, nsAString
& aInitiatorType
,
27 nsAString
& aEntryName
) {
28 MOZ_ASSERT(NS_IsMainThread());
30 // Check if resource timing is prefed off.
31 if (!StaticPrefs::dom_enable_resource_timing()) {
35 if (!aChannel
|| !aTimedChannel
) {
39 bool reportTiming
= true;
40 aTimedChannel
->GetReportResourceTiming(&reportTiming
);
46 aTimedChannel
->GetInitiatorType(aInitiatorType
);
48 // If the initiator type had no valid value, then set it to the default
50 if (aInitiatorType
.IsEmpty()) {
51 aInitiatorType
= u
"other"_ns
;
54 // According to the spec, "The name attribute must return the resolved URL
55 // of the requested resource. This attribute must not change even if the
56 // fetch redirected to a different URL."
57 nsCOMPtr
<nsIURI
> originalURI
;
58 aChannel
->GetOriginalURI(getter_AddRefs(originalURI
));
61 originalURI
->GetSpec(name
);
62 CopyUTF8toUTF16(name
, aEntryName
);
64 // The nsITimedChannel argument will be used to gather all the timings.
65 // The nsIHttpChannel argument will be used to check if any cross-origin
66 // redirects occurred.
67 // The last argument is the "zero time" (offset). Since we don't want
68 // any offset for the resource timing, this will be set to "0" - the
69 // resource timing returns a relative timing (no offset).
70 return new PerformanceTimingData(aTimedChannel
, aChannel
, 0);
73 PerformanceTiming::PerformanceTiming(Performance
* aPerformance
,
74 nsITimedChannel
* aChannel
,
75 nsIHttpChannel
* aHttpChannel
,
76 DOMHighResTimeStamp aZeroTime
)
77 : mPerformance(aPerformance
) {
78 MOZ_ASSERT(aPerformance
, "Parent performance object should be provided");
80 mTimingData
.reset(new PerformanceTimingData(
81 aChannel
, aHttpChannel
,
82 nsRFPService::ReduceTimePrecisionAsMSecs(
83 aZeroTime
, aPerformance
->GetRandomTimelineSeed(),
84 aPerformance
->GetRTPCallerType())));
86 // Non-null aHttpChannel implies that this PerformanceTiming object is being
87 // used for subresources, which is irrelevant to this probe.
88 if (!aHttpChannel
&& StaticPrefs::dom_enable_performance() &&
89 IsTopLevelContentDocument()) {
90 glean::performance_time::response_start
.AccumulateRawDuration(
91 TimeDuration::FromMilliseconds(
92 mTimingData
->ResponseStartHighRes(aPerformance
) -
93 mTimingData
->ZeroTime()));
97 // Copy the timing info from the channel so we don't need to keep the channel
98 // alive just to get the timestamps.
99 PerformanceTimingData::PerformanceTimingData(nsITimedChannel
* aChannel
,
100 nsIHttpChannel
* aHttpChannel
,
101 DOMHighResTimeStamp aZeroTime
)
108 mAllRedirectsSameOrigin(true),
109 mAllRedirectsPassTAO(true),
110 mSecureConnection(false),
111 mTimingAllowed(true),
112 mInitialized(false) {
113 mInitialized
= !!aChannel
;
114 mZeroTime
= aZeroTime
;
116 if (!StaticPrefs::dom_enable_performance()) {
120 nsCOMPtr
<nsIURI
> uri
;
122 aHttpChannel
->GetURI(getter_AddRefs(uri
));
124 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
126 httpChannel
->GetURI(getter_AddRefs(uri
));
131 mSecureConnection
= uri
->SchemeIs("https");
135 aChannel
->GetAsyncOpen(&mAsyncOpen
);
136 aChannel
->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin
);
137 aChannel
->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO
);
138 aChannel
->GetRedirectCount(&mRedirectCount
);
139 aChannel
->GetRedirectStart(&mRedirectStart
);
140 aChannel
->GetRedirectEnd(&mRedirectEnd
);
141 aChannel
->GetDomainLookupStart(&mDomainLookupStart
);
142 aChannel
->GetDomainLookupEnd(&mDomainLookupEnd
);
143 aChannel
->GetConnectStart(&mConnectStart
);
144 aChannel
->GetSecureConnectionStart(&mSecureConnectionStart
);
145 aChannel
->GetConnectEnd(&mConnectEnd
);
146 aChannel
->GetRequestStart(&mRequestStart
);
147 aChannel
->GetResponseStart(&mResponseStart
);
148 aChannel
->GetCacheReadStart(&mCacheReadStart
);
149 aChannel
->GetResponseEnd(&mResponseEnd
);
150 aChannel
->GetCacheReadEnd(&mCacheReadEnd
);
152 aChannel
->GetDispatchFetchEventStart(&mWorkerStart
);
153 aChannel
->GetHandleFetchEventStart(&mWorkerRequestStart
);
154 // TODO: Track when FetchEvent.respondWith() promise resolves as
155 // ServiceWorker interception responseStart?
156 aChannel
->GetHandleFetchEventEnd(&mWorkerResponseEnd
);
158 // The performance timing api essentially requires that the event timestamps
159 // have a strict relation with each other. The truth, however, is the
160 // browser engages in a number of speculative activities that sometimes mean
161 // connections and lookups begin at different times. Workaround that here by
162 // clamping these values to what we expect FetchStart to be. This means the
163 // later of AsyncOpen or WorkerStart times.
164 if (!mAsyncOpen
.IsNull()) {
165 // We want to clamp to the expected FetchStart value. This is later of
166 // the AsyncOpen and WorkerStart values.
167 const TimeStamp
* clampTime
= &mAsyncOpen
;
168 if (!mWorkerStart
.IsNull() && mWorkerStart
> mAsyncOpen
) {
169 clampTime
= &mWorkerStart
;
172 if (!mDomainLookupStart
.IsNull() && mDomainLookupStart
< *clampTime
) {
173 mDomainLookupStart
= *clampTime
;
176 if (!mDomainLookupEnd
.IsNull() && mDomainLookupEnd
< *clampTime
) {
177 mDomainLookupEnd
= *clampTime
;
180 if (!mConnectStart
.IsNull() && mConnectStart
< *clampTime
) {
181 mConnectStart
= *clampTime
;
184 if (mSecureConnection
&& !mSecureConnectionStart
.IsNull() &&
185 mSecureConnectionStart
< *clampTime
) {
186 mSecureConnectionStart
= *clampTime
;
189 if (!mConnectEnd
.IsNull() && mConnectEnd
< *clampTime
) {
190 mConnectEnd
= *clampTime
;
195 // The aHttpChannel argument is null if this PerformanceTiming object is
196 // being used for navigation timing (which is only relevant for documents).
197 // It has a non-null value if this PerformanceTiming object is being used
198 // for resource timing, which can include document loads, both toplevel and
199 // in subframes, and resources linked from a document.
201 SetPropertiesFromHttpChannel(aHttpChannel
, aChannel
);
205 PerformanceTimingData::PerformanceTimingData(
206 const IPCPerformanceTimingData
& aIPCData
)
207 : mNextHopProtocol(aIPCData
.nextHopProtocol()),
208 mAsyncOpen(aIPCData
.asyncOpen()),
209 mRedirectStart(aIPCData
.redirectStart()),
210 mRedirectEnd(aIPCData
.redirectEnd()),
211 mDomainLookupStart(aIPCData
.domainLookupStart()),
212 mDomainLookupEnd(aIPCData
.domainLookupEnd()),
213 mConnectStart(aIPCData
.connectStart()),
214 mSecureConnectionStart(aIPCData
.secureConnectionStart()),
215 mConnectEnd(aIPCData
.connectEnd()),
216 mRequestStart(aIPCData
.requestStart()),
217 mResponseStart(aIPCData
.responseStart()),
218 mCacheReadStart(aIPCData
.cacheReadStart()),
219 mResponseEnd(aIPCData
.responseEnd()),
220 mCacheReadEnd(aIPCData
.cacheReadEnd()),
221 mWorkerStart(aIPCData
.workerStart()),
222 mWorkerRequestStart(aIPCData
.workerRequestStart()),
223 mWorkerResponseEnd(aIPCData
.workerResponseEnd()),
224 mZeroTime(aIPCData
.zeroTime()),
225 mFetchStart(aIPCData
.fetchStart()),
226 mEncodedBodySize(aIPCData
.encodedBodySize()),
227 mTransferSize(aIPCData
.transferSize()),
228 mDecodedBodySize(aIPCData
.decodedBodySize()),
229 mRedirectCount(aIPCData
.redirectCount()),
230 mAllRedirectsSameOrigin(aIPCData
.allRedirectsSameOrigin()),
231 mAllRedirectsPassTAO(aIPCData
.allRedirectsPassTAO()),
232 mSecureConnection(aIPCData
.secureConnection()),
233 mTimingAllowed(aIPCData
.timingAllowed()),
234 mInitialized(aIPCData
.initialized()) {
235 for (const auto& serverTimingData
: aIPCData
.serverTiming()) {
236 RefPtr
<nsServerTiming
> timing
= new nsServerTiming();
237 timing
->SetName(serverTimingData
.name());
238 timing
->SetDuration(serverTimingData
.duration());
239 timing
->SetDescription(serverTimingData
.description());
240 mServerTiming
.AppendElement(timing
);
244 IPCPerformanceTimingData
PerformanceTimingData::ToIPC() {
245 nsTArray
<IPCServerTiming
> ipcServerTiming
;
246 for (auto& serverTimingData
: mServerTiming
) {
248 Unused
<< serverTimingData
->GetName(name
);
250 Unused
<< serverTimingData
->GetDuration(&duration
);
251 nsAutoCString description
;
252 Unused
<< serverTimingData
->GetDescription(description
);
253 ipcServerTiming
.AppendElement(IPCServerTiming(name
, duration
, description
));
255 return IPCPerformanceTimingData(
256 ipcServerTiming
, mNextHopProtocol
, mAsyncOpen
, mRedirectStart
,
257 mRedirectEnd
, mDomainLookupStart
, mDomainLookupEnd
, mConnectStart
,
258 mSecureConnectionStart
, mConnectEnd
, mRequestStart
, mResponseStart
,
259 mCacheReadStart
, mResponseEnd
, mCacheReadEnd
, mWorkerStart
,
260 mWorkerRequestStart
, mWorkerResponseEnd
, mZeroTime
, mFetchStart
,
261 mEncodedBodySize
, mTransferSize
, mDecodedBodySize
, mRedirectCount
,
262 mAllRedirectsSameOrigin
, mAllRedirectsPassTAO
, mSecureConnection
,
263 mTimingAllowed
, mInitialized
);
266 void PerformanceTimingData::SetPropertiesFromHttpChannel(
267 nsIHttpChannel
* aHttpChannel
, nsITimedChannel
* aChannel
) {
268 MOZ_ASSERT(aHttpChannel
);
270 nsAutoCString protocol
;
271 Unused
<< aHttpChannel
->GetProtocolVersion(protocol
);
272 CopyUTF8toUTF16(protocol
, mNextHopProtocol
);
274 Unused
<< aHttpChannel
->GetEncodedBodySize(&mEncodedBodySize
);
275 Unused
<< aHttpChannel
->GetTransferSize(&mTransferSize
);
276 Unused
<< aHttpChannel
->GetDecodedBodySize(&mDecodedBodySize
);
277 if (mDecodedBodySize
== 0) {
278 mDecodedBodySize
= mEncodedBodySize
;
281 mTimingAllowed
= CheckAllowedOrigin(aHttpChannel
, aChannel
);
282 aChannel
->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO
);
284 aChannel
->GetNativeServerTiming(mServerTiming
);
287 PerformanceTiming::~PerformanceTiming() = default;
289 DOMHighResTimeStamp
PerformanceTimingData::FetchStartHighRes(
290 Performance
* aPerformance
) {
291 MOZ_ASSERT(aPerformance
);
294 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
297 MOZ_ASSERT(!mAsyncOpen
.IsNull(),
298 "The fetch start time stamp should always be "
299 "valid if the performance timing is enabled");
300 if (!mAsyncOpen
.IsNull()) {
301 if (!mWorkerRequestStart
.IsNull() && mWorkerRequestStart
> mAsyncOpen
) {
302 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mWorkerRequestStart
);
304 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
308 return nsRFPService::ReduceTimePrecisionAsMSecs(
309 mFetchStart
, aPerformance
->GetRandomTimelineSeed(),
310 aPerformance
->GetRTPCallerType());
313 DOMTimeMilliSec
PerformanceTiming::FetchStart() {
314 return static_cast<int64_t>(mTimingData
->FetchStartHighRes(mPerformance
));
317 bool PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel
* aResourceChannel
,
318 nsITimedChannel
* aChannel
) {
319 if (!IsInitialized()) {
323 // Check that the current document passes the ckeck.
324 nsCOMPtr
<nsILoadInfo
> loadInfo
= aResourceChannel
->LoadInfo();
326 // TYPE_DOCUMENT loads have no loadingPrincipal.
327 if (loadInfo
->GetExternalContentPolicyType() ==
328 ExtContentPolicy::TYPE_DOCUMENT
) {
332 nsCOMPtr
<nsIPrincipal
> principal
= loadInfo
->GetLoadingPrincipal();
334 // Check if the resource is either same origin as the page that started
335 // the load, or if the response contains the proper Timing-Allow-Origin
336 // header with the domain of the page that started the load.
337 return aChannel
->TimingAllowCheck(principal
);
340 uint8_t PerformanceTimingData::GetRedirectCount() const {
341 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
344 if (!mAllRedirectsSameOrigin
) {
347 return mRedirectCount
;
350 bool PerformanceTimingData::ShouldReportCrossOriginRedirect(
351 bool aEnsureSameOriginAndIgnoreTAO
) const {
352 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
356 if (!mTimingAllowed
|| mRedirectCount
== 0) {
360 // If the redirect count is 0, or if one of the cross-origin
361 // redirects doesn't have the proper Timing-Allow-Origin header,
362 // then RedirectStart and RedirectEnd will be set to zero
363 return aEnsureSameOriginAndIgnoreTAO
? mAllRedirectsSameOrigin
364 : mAllRedirectsPassTAO
;
367 DOMHighResTimeStamp
PerformanceTimingData::AsyncOpenHighRes(
368 Performance
* aPerformance
) {
369 MOZ_ASSERT(aPerformance
);
371 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
372 mAsyncOpen
.IsNull()) {
375 DOMHighResTimeStamp rawValue
=
376 TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
377 return nsRFPService::ReduceTimePrecisionAsMSecs(
378 rawValue
, aPerformance
->GetRandomTimelineSeed(),
379 aPerformance
->GetRTPCallerType());
382 DOMHighResTimeStamp
PerformanceTimingData::WorkerStartHighRes(
383 Performance
* aPerformance
) {
384 MOZ_ASSERT(aPerformance
);
386 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
387 mWorkerStart
.IsNull()) {
390 DOMHighResTimeStamp rawValue
=
391 TimeStampToDOMHighRes(aPerformance
, mWorkerStart
);
392 return nsRFPService::ReduceTimePrecisionAsMSecs(
393 rawValue
, aPerformance
->GetRandomTimelineSeed(),
394 aPerformance
->GetRTPCallerType());
398 * RedirectStartHighRes() is used by both the navigation timing and the
399 * resource timing. Since, navigation timing and resource timing check and
400 * interpret cross-domain redirects in a different manner,
401 * RedirectStartHighRes() will make no checks for cross-domain redirect.
402 * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
403 * and PerformanceResourceTiming::RedirectStart() to make such verifications.
405 * @return a valid timing if the Performance Timing is enabled
407 DOMHighResTimeStamp
PerformanceTimingData::RedirectStartHighRes(
408 Performance
* aPerformance
) {
409 MOZ_ASSERT(aPerformance
);
411 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
414 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectStart
);
417 DOMTimeMilliSec
PerformanceTiming::RedirectStart() {
418 if (!mTimingData
->IsInitialized()) {
421 // We have to check if all the redirect URIs had the same origin (since there
422 // is no check in RedirectStartHighRes())
423 if (mTimingData
->AllRedirectsSameOrigin() &&
424 mTimingData
->RedirectCountReal()) {
425 return static_cast<int64_t>(
426 mTimingData
->RedirectStartHighRes(mPerformance
));
432 * RedirectEndHighRes() is used by both the navigation timing and the resource
433 * timing. Since, navigation timing and resource timing check and interpret
434 * cross-domain redirects in a different manner, RedirectEndHighRes() will make
435 * no checks for cross-domain redirect. It's up to the consumers of this method
436 * (PerformanceTiming::RedirectEnd() and
437 * PerformanceResourceTiming::RedirectEnd() to make such verifications.
439 * @return a valid timing if the Performance Timing is enabled
441 DOMHighResTimeStamp
PerformanceTimingData::RedirectEndHighRes(
442 Performance
* aPerformance
) {
443 MOZ_ASSERT(aPerformance
);
445 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
448 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectEnd
);
451 DOMTimeMilliSec
PerformanceTiming::RedirectEnd() {
452 if (!mTimingData
->IsInitialized()) {
455 // We have to check if all the redirect URIs had the same origin (since there
456 // is no check in RedirectEndHighRes())
457 if (mTimingData
->AllRedirectsSameOrigin() &&
458 mTimingData
->RedirectCountReal()) {
459 return static_cast<int64_t>(mTimingData
->RedirectEndHighRes(mPerformance
));
464 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupStartHighRes(
465 Performance
* aPerformance
) {
466 MOZ_ASSERT(aPerformance
);
468 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
471 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
472 if (aPerformance
->ShouldResistFingerprinting()) {
473 return FetchStartHighRes(aPerformance
);
475 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
,
479 DOMTimeMilliSec
PerformanceTiming::DomainLookupStart() {
480 return static_cast<int64_t>(
481 mTimingData
->DomainLookupStartHighRes(mPerformance
));
484 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupEndHighRes(
485 Performance
* aPerformance
) {
486 MOZ_ASSERT(aPerformance
);
488 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
491 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
492 if (aPerformance
->ShouldResistFingerprinting()) {
493 return FetchStartHighRes(aPerformance
);
495 // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
496 if (mDomainLookupEnd
.IsNull()) {
497 return DomainLookupStartHighRes(aPerformance
);
499 DOMHighResTimeStamp rawValue
=
500 TimeStampToDOMHighRes(aPerformance
, mDomainLookupEnd
);
501 return nsRFPService::ReduceTimePrecisionAsMSecs(
502 rawValue
, aPerformance
->GetRandomTimelineSeed(),
503 aPerformance
->GetRTPCallerType());
506 DOMTimeMilliSec
PerformanceTiming::DomainLookupEnd() {
507 return static_cast<int64_t>(
508 mTimingData
->DomainLookupEndHighRes(mPerformance
));
511 DOMHighResTimeStamp
PerformanceTimingData::ConnectStartHighRes(
512 Performance
* aPerformance
) {
513 MOZ_ASSERT(aPerformance
);
515 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
518 if (mConnectStart
.IsNull()) {
519 return DomainLookupEndHighRes(aPerformance
);
521 DOMHighResTimeStamp rawValue
=
522 TimeStampToDOMHighRes(aPerformance
, mConnectStart
);
523 return nsRFPService::ReduceTimePrecisionAsMSecs(
524 rawValue
, aPerformance
->GetRandomTimelineSeed(),
525 aPerformance
->GetRTPCallerType());
528 DOMTimeMilliSec
PerformanceTiming::ConnectStart() {
529 return static_cast<int64_t>(mTimingData
->ConnectStartHighRes(mPerformance
));
532 DOMHighResTimeStamp
PerformanceTimingData::SecureConnectionStartHighRes(
533 Performance
* aPerformance
) {
534 MOZ_ASSERT(aPerformance
);
536 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
539 if (!mSecureConnection
) {
540 return 0; // We use 0 here, because mZeroTime is sometimes set to the
541 // navigation start time.
543 if (mSecureConnectionStart
.IsNull()) {
544 return ConnectStartHighRes(aPerformance
);
546 DOMHighResTimeStamp rawValue
=
547 TimeStampToDOMHighRes(aPerformance
, mSecureConnectionStart
);
548 return nsRFPService::ReduceTimePrecisionAsMSecs(
549 rawValue
, aPerformance
->GetRandomTimelineSeed(),
550 aPerformance
->GetRTPCallerType());
553 DOMTimeMilliSec
PerformanceTiming::SecureConnectionStart() {
554 return static_cast<int64_t>(
555 mTimingData
->SecureConnectionStartHighRes(mPerformance
));
558 DOMHighResTimeStamp
PerformanceTimingData::ConnectEndHighRes(
559 Performance
* aPerformance
) {
560 MOZ_ASSERT(aPerformance
);
562 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
565 // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
566 if (mConnectEnd
.IsNull()) {
567 return ConnectStartHighRes(aPerformance
);
569 DOMHighResTimeStamp rawValue
=
570 TimeStampToDOMHighRes(aPerformance
, mConnectEnd
);
571 return nsRFPService::ReduceTimePrecisionAsMSecs(
572 rawValue
, aPerformance
->GetRandomTimelineSeed(),
573 aPerformance
->GetRTPCallerType());
576 DOMTimeMilliSec
PerformanceTiming::ConnectEnd() {
577 return static_cast<int64_t>(mTimingData
->ConnectEndHighRes(mPerformance
));
580 DOMHighResTimeStamp
PerformanceTimingData::RequestStartHighRes(
581 Performance
* aPerformance
) {
582 MOZ_ASSERT(aPerformance
);
584 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
588 if (mRequestStart
.IsNull()) {
589 mRequestStart
= mWorkerRequestStart
;
592 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRequestStart
);
595 DOMTimeMilliSec
PerformanceTiming::RequestStart() {
596 return static_cast<int64_t>(mTimingData
->RequestStartHighRes(mPerformance
));
599 DOMHighResTimeStamp
PerformanceTimingData::ResponseStartHighRes(
600 Performance
* aPerformance
) {
601 MOZ_ASSERT(aPerformance
);
603 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
606 if (mResponseStart
.IsNull() ||
607 (!mCacheReadStart
.IsNull() && mCacheReadStart
< mResponseStart
)) {
608 mResponseStart
= mCacheReadStart
;
611 if (mResponseStart
.IsNull() ||
612 (!mRequestStart
.IsNull() && mResponseStart
< mRequestStart
)) {
613 mResponseStart
= mRequestStart
;
615 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mResponseStart
);
618 DOMTimeMilliSec
PerformanceTiming::ResponseStart() {
619 return static_cast<int64_t>(mTimingData
->ResponseStartHighRes(mPerformance
));
622 DOMHighResTimeStamp
PerformanceTimingData::ResponseEndHighRes(
623 Performance
* aPerformance
) {
624 MOZ_ASSERT(aPerformance
);
626 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
629 if (mResponseEnd
.IsNull() ||
630 (!mCacheReadEnd
.IsNull() && mCacheReadEnd
< mResponseEnd
)) {
631 mResponseEnd
= mCacheReadEnd
;
633 if (mResponseEnd
.IsNull()) {
634 mResponseEnd
= mWorkerResponseEnd
;
636 // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
637 if (mResponseEnd
.IsNull()) {
638 return ResponseStartHighRes(aPerformance
);
640 DOMHighResTimeStamp rawValue
=
641 TimeStampToDOMHighRes(aPerformance
, mResponseEnd
);
642 return nsRFPService::ReduceTimePrecisionAsMSecs(
643 rawValue
, aPerformance
->GetRandomTimelineSeed(),
644 aPerformance
->GetRTPCallerType());
647 DOMTimeMilliSec
PerformanceTiming::ResponseEnd() {
648 return static_cast<int64_t>(mTimingData
->ResponseEndHighRes(mPerformance
));
651 JSObject
* PerformanceTiming::WrapObject(JSContext
* cx
,
652 JS::Handle
<JSObject
*> aGivenProto
) {
653 return PerformanceTiming_Binding::Wrap(cx
, this, aGivenProto
);
656 bool PerformanceTiming::IsTopLevelContentDocument() const {
657 nsCOMPtr
<Document
> document
= mPerformance
->GetDocumentIfCurrent();
662 if (BrowsingContext
* bc
= document
->GetBrowsingContext()) {
663 return bc
->IsTopContent();
668 nsTArray
<nsCOMPtr
<nsIServerTiming
>> PerformanceTimingData::GetServerTiming() {
669 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
671 return nsTArray
<nsCOMPtr
<nsIServerTiming
>>();
674 return mServerTiming
.Clone();
677 } // namespace mozilla::dom