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/FragmentDirective.h"
11 #include "mozilla/dom/PerformanceResourceTimingBinding.h"
12 #include "mozilla/dom/PerformanceTimingBinding.h"
13 #include "mozilla/glean/GleanMetrics.h"
14 #include "nsIDocShell.h"
15 #include "nsIDocShellTreeItem.h"
16 #include "nsIHttpChannel.h"
17 #include "mozilla/dom/BrowsingContext.h"
18 #include "mozilla/dom/Document.h"
19 #include "nsITimedChannel.h"
21 namespace mozilla::dom
{
23 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming
, mPerformance
)
26 PerformanceTimingData
* PerformanceTimingData::Create(
27 nsITimedChannel
* aTimedChannel
, nsIHttpChannel
* aChannel
,
28 DOMHighResTimeStamp aZeroTime
, nsAString
& aInitiatorType
,
29 nsAString
& aEntryName
) {
30 MOZ_ASSERT(NS_IsMainThread());
32 // Check if resource timing is prefed off.
33 if (!StaticPrefs::dom_enable_resource_timing()) {
37 if (!aChannel
|| !aTimedChannel
) {
41 bool reportTiming
= true;
42 aTimedChannel
->GetReportResourceTiming(&reportTiming
);
48 aTimedChannel
->GetInitiatorType(aInitiatorType
);
50 // If the initiator type had no valid value, then set it to the default
52 if (aInitiatorType
.IsEmpty()) {
53 aInitiatorType
= u
"other"_ns
;
56 // According to the spec, "The name attribute must return the resolved URL
57 // of the requested resource. This attribute must not change even if the
58 // fetch redirected to a different URL."
59 nsCOMPtr
<nsIURI
> originalURI
;
60 aChannel
->GetOriginalURI(getter_AddRefs(originalURI
));
63 FragmentDirective::GetSpecIgnoringFragmentDirective(originalURI
, name
);
64 CopyUTF8toUTF16(name
, aEntryName
);
66 // The nsITimedChannel argument will be used to gather all the timings.
67 // The nsIHttpChannel argument will be used to check if any cross-origin
68 // redirects occurred.
69 // The last argument is the "zero time" (offset). Since we don't want
70 // any offset for the resource timing, this will be set to "0" - the
71 // resource timing returns a relative timing (no offset).
72 return new PerformanceTimingData(aTimedChannel
, aChannel
, 0);
76 PerformanceTimingData
* PerformanceTimingData::Create(
77 const CacheablePerformanceTimingData
& aCachedData
,
78 DOMHighResTimeStamp aZeroTime
, TimeStamp aStartTime
, TimeStamp aEndTime
,
79 RenderBlockingStatusType aRenderBlockingStatus
) {
80 MOZ_ASSERT(NS_IsMainThread());
82 // Check if resource timing is prefed off.
83 if (!StaticPrefs::dom_enable_resource_timing()) {
87 return new PerformanceTimingData(aCachedData
, aZeroTime
, aStartTime
, aEndTime
,
88 aRenderBlockingStatus
);
91 PerformanceTiming::PerformanceTiming(Performance
* aPerformance
,
92 nsITimedChannel
* aChannel
,
93 nsIHttpChannel
* aHttpChannel
,
94 DOMHighResTimeStamp aZeroTime
)
95 : mPerformance(aPerformance
) {
96 MOZ_ASSERT(aPerformance
, "Parent performance object should be provided");
98 mTimingData
.reset(new PerformanceTimingData(
99 aChannel
, aHttpChannel
,
100 nsRFPService::ReduceTimePrecisionAsMSecs(
101 aZeroTime
, aPerformance
->GetRandomTimelineSeed(),
102 aPerformance
->GetRTPCallerType())));
104 // Non-null aHttpChannel implies that this PerformanceTiming object is being
105 // used for subresources, which is irrelevant to this probe.
106 if (!aHttpChannel
&& StaticPrefs::dom_enable_performance() &&
107 IsTopLevelContentDocument()) {
108 glean::performance_time::response_start
.AccumulateRawDuration(
109 TimeDuration::FromMilliseconds(
110 mTimingData
->ResponseStartHighRes(aPerformance
) -
111 mTimingData
->ZeroTime()));
115 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
116 nsITimedChannel
* aChannel
, nsIHttpChannel
* aHttpChannel
)
117 : mEncodedBodySize(0),
121 mAllRedirectsSameOrigin(true),
122 mAllRedirectsPassTAO(true),
123 mSecureConnection(false),
124 mTimingAllowed(true),
125 mInitialized(false) {
126 mInitialized
= !!aChannel
;
128 nsCOMPtr
<nsIURI
> uri
;
130 aHttpChannel
->GetURI(getter_AddRefs(uri
));
132 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
134 httpChannel
->GetURI(getter_AddRefs(uri
));
139 mSecureConnection
= uri
->SchemeIs("https");
143 aChannel
->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin
);
144 aChannel
->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO
);
145 aChannel
->GetRedirectCount(&mRedirectCount
);
148 // The aHttpChannel argument is null if this PerformanceTiming object is
149 // being used for navigation timing (which is only relevant for documents).
150 // It has a non-null value if this PerformanceTiming object is being used
151 // for resource timing, which can include document loads, both toplevel and
152 // in subframes, and resources linked from a document.
154 SetCacheablePropertiesFromHttpChannel(aHttpChannel
, aChannel
);
158 // Copy the timing info from the channel so we don't need to keep the channel
159 // alive just to get the timestamps.
160 PerformanceTimingData::PerformanceTimingData(nsITimedChannel
* aChannel
,
161 nsIHttpChannel
* aHttpChannel
,
162 DOMHighResTimeStamp aZeroTime
)
163 : CacheablePerformanceTimingData(aChannel
, aHttpChannel
),
167 mZeroTime
= aZeroTime
;
169 if (!StaticPrefs::dom_enable_performance()) {
174 aChannel
->GetAsyncOpen(&mAsyncOpen
);
175 aChannel
->GetRedirectStart(&mRedirectStart
);
176 aChannel
->GetRedirectEnd(&mRedirectEnd
);
177 aChannel
->GetDomainLookupStart(&mDomainLookupStart
);
178 aChannel
->GetDomainLookupEnd(&mDomainLookupEnd
);
179 aChannel
->GetConnectStart(&mConnectStart
);
180 aChannel
->GetSecureConnectionStart(&mSecureConnectionStart
);
181 aChannel
->GetConnectEnd(&mConnectEnd
);
182 aChannel
->GetRequestStart(&mRequestStart
);
183 aChannel
->GetResponseStart(&mResponseStart
);
184 aChannel
->GetCacheReadStart(&mCacheReadStart
);
185 aChannel
->GetResponseEnd(&mResponseEnd
);
186 aChannel
->GetCacheReadEnd(&mCacheReadEnd
);
188 aChannel
->GetDispatchFetchEventStart(&mWorkerStart
);
189 aChannel
->GetHandleFetchEventStart(&mWorkerRequestStart
);
190 // TODO: Track when FetchEvent.respondWith() promise resolves as
191 // ServiceWorker interception responseStart?
192 aChannel
->GetHandleFetchEventEnd(&mWorkerResponseEnd
);
194 // The performance timing api essentially requires that the event timestamps
195 // have a strict relation with each other. The truth, however, is the
196 // browser engages in a number of speculative activities that sometimes mean
197 // connections and lookups begin at different times. Workaround that here by
198 // clamping these values to what we expect FetchStart to be. This means the
199 // later of AsyncOpen or WorkerStart times.
200 if (!mAsyncOpen
.IsNull()) {
201 // We want to clamp to the expected FetchStart value. This is later of
202 // the AsyncOpen and WorkerStart values.
203 const TimeStamp
* clampTime
= &mAsyncOpen
;
204 if (!mWorkerStart
.IsNull() && mWorkerStart
> mAsyncOpen
) {
205 clampTime
= &mWorkerStart
;
208 if (!mDomainLookupStart
.IsNull() && mDomainLookupStart
< *clampTime
) {
209 mDomainLookupStart
= *clampTime
;
212 if (!mDomainLookupEnd
.IsNull() && mDomainLookupEnd
< *clampTime
) {
213 mDomainLookupEnd
= *clampTime
;
216 if (!mConnectStart
.IsNull() && mConnectStart
< *clampTime
) {
217 mConnectStart
= *clampTime
;
220 if (mSecureConnection
&& !mSecureConnectionStart
.IsNull() &&
221 mSecureConnectionStart
< *clampTime
) {
222 mSecureConnectionStart
= *clampTime
;
225 if (!mConnectEnd
.IsNull() && mConnectEnd
< *clampTime
) {
226 mConnectEnd
= *clampTime
;
232 // NOTE: Other fields are set by SetCacheablePropertiesFromHttpChannel,
233 // called inside CacheablePerformanceTimingData constructor.
234 SetTransferSizeFromHttpChannel(aHttpChannel
);
237 bool renderBlocking
= false;
239 aChannel
->GetRenderBlocking(&renderBlocking
);
241 mRenderBlockingStatus
= renderBlocking
242 ? RenderBlockingStatusType::Blocking
243 : RenderBlockingStatusType::Non_blocking
;
246 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
247 const CacheablePerformanceTimingData
& aOther
)
248 : mEncodedBodySize(aOther
.mEncodedBodySize
),
249 mDecodedBodySize(aOther
.mDecodedBodySize
),
250 mResponseStatus(aOther
.mResponseStatus
),
251 mRedirectCount(aOther
.mRedirectCount
),
252 mBodyInfoAccessAllowed(aOther
.mBodyInfoAccessAllowed
),
253 mAllRedirectsSameOrigin(aOther
.mAllRedirectsSameOrigin
),
254 mAllRedirectsPassTAO(aOther
.mAllRedirectsPassTAO
),
255 mSecureConnection(aOther
.mSecureConnection
),
256 mTimingAllowed(aOther
.mTimingAllowed
),
257 mInitialized(aOther
.mInitialized
),
258 mNextHopProtocol(aOther
.mNextHopProtocol
),
259 mContentType(aOther
.mContentType
) {
260 for (auto& data
: aOther
.mServerTiming
) {
261 mServerTiming
.AppendElement(data
);
265 PerformanceTimingData::PerformanceTimingData(
266 const CacheablePerformanceTimingData
& aCachedData
,
267 DOMHighResTimeStamp aZeroTime
, TimeStamp aStartTime
, TimeStamp aEndTime
,
268 RenderBlockingStatusType aRenderBlockingStatus
)
269 : CacheablePerformanceTimingData(aCachedData
),
270 mAsyncOpen(aStartTime
),
271 mResponseEnd(aEndTime
),
272 mZeroTime(aZeroTime
),
273 mTransferSize(kLocalCacheTransferSize
),
274 mRenderBlockingStatus(aRenderBlockingStatus
) {
275 if (!StaticPrefs::dom_enable_performance()) {
280 CacheablePerformanceTimingData::CacheablePerformanceTimingData(
281 const IPCPerformanceTimingData
& aIPCData
)
282 : mEncodedBodySize(aIPCData
.encodedBodySize()),
283 mDecodedBodySize(aIPCData
.decodedBodySize()),
284 mResponseStatus(aIPCData
.responseStatus()),
285 mRedirectCount(aIPCData
.redirectCount()),
286 mBodyInfoAccessAllowed(aIPCData
.bodyInfoAccessAllowed()),
287 mAllRedirectsSameOrigin(aIPCData
.allRedirectsSameOrigin()),
288 mAllRedirectsPassTAO(aIPCData
.allRedirectsPassTAO()),
289 mSecureConnection(aIPCData
.secureConnection()),
290 mTimingAllowed(aIPCData
.timingAllowed()),
291 mInitialized(aIPCData
.initialized()),
292 mNextHopProtocol(aIPCData
.nextHopProtocol()),
293 mContentType(aIPCData
.contentType()) {
294 for (const auto& serverTimingData
: aIPCData
.serverTiming()) {
295 RefPtr
<nsServerTiming
> timing
= new nsServerTiming();
296 timing
->SetName(serverTimingData
.name());
297 timing
->SetDuration(serverTimingData
.duration());
298 timing
->SetDescription(serverTimingData
.description());
299 mServerTiming
.AppendElement(timing
);
303 PerformanceTimingData::PerformanceTimingData(
304 const IPCPerformanceTimingData
& aIPCData
)
305 : CacheablePerformanceTimingData(aIPCData
),
306 mAsyncOpen(aIPCData
.asyncOpen()),
307 mRedirectStart(aIPCData
.redirectStart()),
308 mRedirectEnd(aIPCData
.redirectEnd()),
309 mDomainLookupStart(aIPCData
.domainLookupStart()),
310 mDomainLookupEnd(aIPCData
.domainLookupEnd()),
311 mConnectStart(aIPCData
.connectStart()),
312 mSecureConnectionStart(aIPCData
.secureConnectionStart()),
313 mConnectEnd(aIPCData
.connectEnd()),
314 mRequestStart(aIPCData
.requestStart()),
315 mResponseStart(aIPCData
.responseStart()),
316 mCacheReadStart(aIPCData
.cacheReadStart()),
317 mResponseEnd(aIPCData
.responseEnd()),
318 mCacheReadEnd(aIPCData
.cacheReadEnd()),
319 mWorkerStart(aIPCData
.workerStart()),
320 mWorkerRequestStart(aIPCData
.workerRequestStart()),
321 mWorkerResponseEnd(aIPCData
.workerResponseEnd()),
322 mZeroTime(aIPCData
.zeroTime()),
323 mFetchStart(aIPCData
.fetchStart()),
324 mTransferSize(aIPCData
.transferSize()),
325 mRenderBlockingStatus(aIPCData
.renderBlocking()
326 ? RenderBlockingStatusType::Blocking
327 : RenderBlockingStatusType::Non_blocking
) {}
329 IPCPerformanceTimingData
PerformanceTimingData::ToIPC() {
330 nsTArray
<IPCServerTiming
> ipcServerTiming
;
331 for (auto& serverTimingData
: mServerTiming
) {
333 Unused
<< serverTimingData
->GetName(name
);
335 Unused
<< serverTimingData
->GetDuration(&duration
);
336 nsAutoCString description
;
337 Unused
<< serverTimingData
->GetDescription(description
);
338 ipcServerTiming
.AppendElement(IPCServerTiming(name
, duration
, description
));
340 bool renderBlocking
=
341 mRenderBlockingStatus
== RenderBlockingStatusType::Blocking
;
342 return IPCPerformanceTimingData(
343 ipcServerTiming
, mNextHopProtocol
, mAsyncOpen
, mRedirectStart
,
344 mRedirectEnd
, mDomainLookupStart
, mDomainLookupEnd
, mConnectStart
,
345 mSecureConnectionStart
, mConnectEnd
, mRequestStart
, mResponseStart
,
346 mCacheReadStart
, mResponseEnd
, mCacheReadEnd
, mWorkerStart
,
347 mWorkerRequestStart
, mWorkerResponseEnd
, mZeroTime
, mFetchStart
,
348 mEncodedBodySize
, mTransferSize
, mDecodedBodySize
, mResponseStatus
,
349 mRedirectCount
, renderBlocking
, mContentType
, mAllRedirectsSameOrigin
,
350 mAllRedirectsPassTAO
, mSecureConnection
, mBodyInfoAccessAllowed
,
351 mTimingAllowed
, mInitialized
);
354 void CacheablePerformanceTimingData::SetCacheablePropertiesFromHttpChannel(
355 nsIHttpChannel
* aHttpChannel
, nsITimedChannel
* aChannel
) {
356 MOZ_ASSERT(aHttpChannel
);
358 nsAutoCString protocol
;
359 Unused
<< aHttpChannel
->GetProtocolVersion(protocol
);
360 CopyUTF8toUTF16(protocol
, mNextHopProtocol
);
362 Unused
<< aHttpChannel
->GetEncodedBodySize(&mEncodedBodySize
);
363 Unused
<< aHttpChannel
->GetDecodedBodySize(&mDecodedBodySize
);
364 if (mDecodedBodySize
== 0) {
365 mDecodedBodySize
= mEncodedBodySize
;
368 uint32_t responseStatus
= 0;
369 Unused
<< aHttpChannel
->GetResponseStatus(&responseStatus
);
370 mResponseStatus
= static_cast<uint16_t>(responseStatus
);
372 nsAutoCString contentType
;
373 Unused
<< aHttpChannel
->GetContentType(contentType
);
374 CopyUTF8toUTF16(contentType
, mContentType
);
376 mBodyInfoAccessAllowed
=
377 CheckBodyInfoAccessAllowedForOrigin(aHttpChannel
, aChannel
);
378 mTimingAllowed
= CheckTimingAllowedForOrigin(aHttpChannel
, aChannel
);
379 aChannel
->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO
);
381 aChannel
->GetNativeServerTiming(mServerTiming
);
384 void PerformanceTimingData::SetPropertiesFromHttpChannel(
385 nsIHttpChannel
* aHttpChannel
, nsITimedChannel
* aChannel
) {
386 SetCacheablePropertiesFromHttpChannel(aHttpChannel
, aChannel
);
387 SetTransferSizeFromHttpChannel(aHttpChannel
);
390 void PerformanceTimingData::SetTransferSizeFromHttpChannel(
391 nsIHttpChannel
* aHttpChannel
) {
392 Unused
<< aHttpChannel
->GetTransferSize(&mTransferSize
);
395 PerformanceTiming::~PerformanceTiming() = default;
397 DOMHighResTimeStamp
PerformanceTimingData::FetchStartHighRes(
398 Performance
* aPerformance
) {
399 MOZ_ASSERT(aPerformance
);
402 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
405 MOZ_ASSERT(!mAsyncOpen
.IsNull(),
406 "The fetch start time stamp should always be "
407 "valid if the performance timing is enabled");
408 if (!mAsyncOpen
.IsNull()) {
409 if (!mWorkerRequestStart
.IsNull() && mWorkerRequestStart
> mAsyncOpen
) {
410 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mWorkerRequestStart
);
412 mFetchStart
= TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
416 return nsRFPService::ReduceTimePrecisionAsMSecs(
417 mFetchStart
, aPerformance
->GetRandomTimelineSeed(),
418 aPerformance
->GetRTPCallerType());
421 DOMTimeMilliSec
PerformanceTiming::FetchStart() {
422 return static_cast<int64_t>(mTimingData
->FetchStartHighRes(mPerformance
));
425 nsITimedChannel::BodyInfoAccess
426 CacheablePerformanceTimingData::CheckBodyInfoAccessAllowedForOrigin(
427 nsIHttpChannel
* aResourceChannel
, nsITimedChannel
* aChannel
) {
428 // Check if the resource is either same origin as the page that started
429 // the load, or if the response contains an Access-Control-Allow-Origin
430 // header with the domain of the page that started the load.
431 MOZ_ASSERT(aChannel
);
433 if (!IsInitialized()) {
434 return nsITimedChannel::BodyInfoAccess::DISALLOWED
;
437 // Check that the current document passes the check.
438 nsCOMPtr
<nsILoadInfo
> loadInfo
= aResourceChannel
->LoadInfo();
440 // TYPE_DOCUMENT loads have no loadingPrincipal.
441 if (loadInfo
->GetExternalContentPolicyType() ==
442 ExtContentPolicy::TYPE_DOCUMENT
) {
443 return nsITimedChannel::BodyInfoAccess::ALLOW_ALL
;
446 nsCOMPtr
<nsIPrincipal
> principal
= loadInfo
->GetLoadingPrincipal();
448 return nsITimedChannel::BodyInfoAccess::DISALLOWED
;
450 return aChannel
->BodyInfoAccessAllowedCheck(principal
);
453 bool CacheablePerformanceTimingData::CheckTimingAllowedForOrigin(
454 nsIHttpChannel
* aResourceChannel
, nsITimedChannel
* aChannel
) {
455 // Check if the resource is either same origin as the page that started
456 // the load, or if the response contains the proper Timing-Allow-Origin
457 // header with the domain of the page that started the load.
458 MOZ_ASSERT(aChannel
);
460 if (!IsInitialized()) {
464 // Check that the current document passes the check.
465 nsCOMPtr
<nsILoadInfo
> loadInfo
= aResourceChannel
->LoadInfo();
467 // TYPE_DOCUMENT loads have no loadingPrincipal.
468 if (loadInfo
->GetExternalContentPolicyType() ==
469 ExtContentPolicy::TYPE_DOCUMENT
) {
473 nsCOMPtr
<nsIPrincipal
> principal
= loadInfo
->GetLoadingPrincipal();
474 return principal
&& aChannel
->TimingAllowCheck(principal
);
477 uint8_t CacheablePerformanceTimingData::GetRedirectCount() const {
478 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
481 if (!mAllRedirectsSameOrigin
) {
484 return mRedirectCount
;
487 bool PerformanceTimingData::ShouldReportCrossOriginRedirect(
488 bool aEnsureSameOriginAndIgnoreTAO
) const {
489 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
493 if (!mTimingAllowed
|| mRedirectCount
== 0) {
497 // If the redirect count is 0, or if one of the cross-origin
498 // redirects doesn't have the proper Timing-Allow-Origin header,
499 // then RedirectStart and RedirectEnd will be set to zero
500 return aEnsureSameOriginAndIgnoreTAO
? mAllRedirectsSameOrigin
501 : mAllRedirectsPassTAO
;
504 DOMHighResTimeStamp
PerformanceTimingData::AsyncOpenHighRes(
505 Performance
* aPerformance
) {
506 MOZ_ASSERT(aPerformance
);
508 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
509 mAsyncOpen
.IsNull()) {
512 DOMHighResTimeStamp rawValue
=
513 TimeStampToDOMHighRes(aPerformance
, mAsyncOpen
);
514 return nsRFPService::ReduceTimePrecisionAsMSecs(
515 rawValue
, aPerformance
->GetRandomTimelineSeed(),
516 aPerformance
->GetRTPCallerType());
519 DOMHighResTimeStamp
PerformanceTimingData::WorkerStartHighRes(
520 Performance
* aPerformance
) {
521 MOZ_ASSERT(aPerformance
);
523 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
524 mWorkerStart
.IsNull()) {
527 DOMHighResTimeStamp rawValue
=
528 TimeStampToDOMHighRes(aPerformance
, mWorkerStart
);
529 return nsRFPService::ReduceTimePrecisionAsMSecs(
530 rawValue
, aPerformance
->GetRandomTimelineSeed(),
531 aPerformance
->GetRTPCallerType());
535 * RedirectStartHighRes() is used by both the navigation timing and the
536 * resource timing. Since, navigation timing and resource timing check and
537 * interpret cross-domain redirects in a different manner,
538 * RedirectStartHighRes() will make no checks for cross-domain redirect.
539 * It's up to the consumers of this method (PerformanceTiming::RedirectStart()
540 * and PerformanceResourceTiming::RedirectStart() to make such verifications.
542 * @return a valid timing if the Performance Timing is enabled
544 DOMHighResTimeStamp
PerformanceTimingData::RedirectStartHighRes(
545 Performance
* aPerformance
) {
546 MOZ_ASSERT(aPerformance
);
548 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
551 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectStart
);
554 DOMTimeMilliSec
PerformanceTiming::RedirectStart() {
555 if (!mTimingData
->IsInitialized()) {
558 // We have to check if all the redirect URIs had the same origin (since there
559 // is no check in RedirectStartHighRes())
560 if (mTimingData
->AllRedirectsSameOrigin() &&
561 mTimingData
->RedirectCountReal()) {
562 return static_cast<int64_t>(
563 mTimingData
->RedirectStartHighRes(mPerformance
));
569 * RedirectEndHighRes() is used by both the navigation timing and the resource
570 * timing. Since, navigation timing and resource timing check and interpret
571 * cross-domain redirects in a different manner, RedirectEndHighRes() will make
572 * no checks for cross-domain redirect. It's up to the consumers of this method
573 * (PerformanceTiming::RedirectEnd() and
574 * PerformanceResourceTiming::RedirectEnd() to make such verifications.
576 * @return a valid timing if the Performance Timing is enabled
578 DOMHighResTimeStamp
PerformanceTimingData::RedirectEndHighRes(
579 Performance
* aPerformance
) {
580 MOZ_ASSERT(aPerformance
);
582 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
585 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRedirectEnd
);
588 DOMTimeMilliSec
PerformanceTiming::RedirectEnd() {
589 if (!mTimingData
->IsInitialized()) {
592 // We have to check if all the redirect URIs had the same origin (since there
593 // is no check in RedirectEndHighRes())
594 if (mTimingData
->AllRedirectsSameOrigin() &&
595 mTimingData
->RedirectCountReal()) {
596 return static_cast<int64_t>(mTimingData
->RedirectEndHighRes(mPerformance
));
601 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupStartHighRes(
602 Performance
* aPerformance
) {
603 MOZ_ASSERT(aPerformance
);
605 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
608 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
609 if (aPerformance
->ShouldResistFingerprinting()) {
610 return FetchStartHighRes(aPerformance
);
612 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
,
616 DOMTimeMilliSec
PerformanceTiming::DomainLookupStart() {
617 return static_cast<int64_t>(
618 mTimingData
->DomainLookupStartHighRes(mPerformance
));
621 DOMHighResTimeStamp
PerformanceTimingData::DomainLookupEndHighRes(
622 Performance
* aPerformance
) {
623 MOZ_ASSERT(aPerformance
);
625 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
628 // Bug 1637985 - DomainLookup information may be useful for fingerprinting.
629 if (aPerformance
->ShouldResistFingerprinting()) {
630 return FetchStartHighRes(aPerformance
);
632 // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
633 if (mDomainLookupEnd
.IsNull()) {
634 return DomainLookupStartHighRes(aPerformance
);
636 DOMHighResTimeStamp rawValue
=
637 TimeStampToDOMHighRes(aPerformance
, mDomainLookupEnd
);
638 return nsRFPService::ReduceTimePrecisionAsMSecs(
639 rawValue
, aPerformance
->GetRandomTimelineSeed(),
640 aPerformance
->GetRTPCallerType());
643 DOMTimeMilliSec
PerformanceTiming::DomainLookupEnd() {
644 return static_cast<int64_t>(
645 mTimingData
->DomainLookupEndHighRes(mPerformance
));
648 DOMHighResTimeStamp
PerformanceTimingData::ConnectStartHighRes(
649 Performance
* aPerformance
) {
650 MOZ_ASSERT(aPerformance
);
652 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
655 if (mConnectStart
.IsNull()) {
656 return DomainLookupEndHighRes(aPerformance
);
658 DOMHighResTimeStamp rawValue
=
659 TimeStampToDOMHighRes(aPerformance
, mConnectStart
);
660 return nsRFPService::ReduceTimePrecisionAsMSecs(
661 rawValue
, aPerformance
->GetRandomTimelineSeed(),
662 aPerformance
->GetRTPCallerType());
665 DOMTimeMilliSec
PerformanceTiming::ConnectStart() {
666 return static_cast<int64_t>(mTimingData
->ConnectStartHighRes(mPerformance
));
669 DOMHighResTimeStamp
PerformanceTimingData::SecureConnectionStartHighRes(
670 Performance
* aPerformance
) {
671 MOZ_ASSERT(aPerformance
);
673 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
676 if (!mSecureConnection
) {
677 return 0; // We use 0 here, because mZeroTime is sometimes set to the
678 // navigation start time.
680 if (mSecureConnectionStart
.IsNull()) {
681 return ConnectStartHighRes(aPerformance
);
683 DOMHighResTimeStamp rawValue
=
684 TimeStampToDOMHighRes(aPerformance
, mSecureConnectionStart
);
685 return nsRFPService::ReduceTimePrecisionAsMSecs(
686 rawValue
, aPerformance
->GetRandomTimelineSeed(),
687 aPerformance
->GetRTPCallerType());
690 DOMTimeMilliSec
PerformanceTiming::SecureConnectionStart() {
691 return static_cast<int64_t>(
692 mTimingData
->SecureConnectionStartHighRes(mPerformance
));
695 DOMHighResTimeStamp
PerformanceTimingData::ConnectEndHighRes(
696 Performance
* aPerformance
) {
697 MOZ_ASSERT(aPerformance
);
699 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
702 // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
703 if (mConnectEnd
.IsNull()) {
704 return ConnectStartHighRes(aPerformance
);
706 DOMHighResTimeStamp rawValue
=
707 TimeStampToDOMHighRes(aPerformance
, mConnectEnd
);
708 return nsRFPService::ReduceTimePrecisionAsMSecs(
709 rawValue
, aPerformance
->GetRandomTimelineSeed(),
710 aPerformance
->GetRTPCallerType());
713 DOMTimeMilliSec
PerformanceTiming::ConnectEnd() {
714 return static_cast<int64_t>(mTimingData
->ConnectEndHighRes(mPerformance
));
717 DOMHighResTimeStamp
PerformanceTimingData::RequestStartHighRes(
718 Performance
* aPerformance
) {
719 MOZ_ASSERT(aPerformance
);
721 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
725 if (mRequestStart
.IsNull()) {
726 mRequestStart
= mWorkerRequestStart
;
729 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mRequestStart
);
732 DOMTimeMilliSec
PerformanceTiming::RequestStart() {
733 return static_cast<int64_t>(mTimingData
->RequestStartHighRes(mPerformance
));
736 DOMHighResTimeStamp
PerformanceTimingData::ResponseStartHighRes(
737 Performance
* aPerformance
) {
738 MOZ_ASSERT(aPerformance
);
740 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
743 if (mResponseStart
.IsNull() ||
744 (!mCacheReadStart
.IsNull() && mCacheReadStart
< mResponseStart
)) {
745 mResponseStart
= mCacheReadStart
;
748 if (mResponseStart
.IsNull() ||
749 (!mRequestStart
.IsNull() && mResponseStart
< mRequestStart
)) {
750 mResponseStart
= mRequestStart
;
752 return TimeStampToReducedDOMHighResOrFetchStart(aPerformance
, mResponseStart
);
755 DOMTimeMilliSec
PerformanceTiming::ResponseStart() {
756 return static_cast<int64_t>(mTimingData
->ResponseStartHighRes(mPerformance
));
759 DOMHighResTimeStamp
PerformanceTimingData::ResponseEndHighRes(
760 Performance
* aPerformance
) {
761 MOZ_ASSERT(aPerformance
);
763 if (!StaticPrefs::dom_enable_performance() || !IsInitialized()) {
766 if (mResponseEnd
.IsNull() ||
767 (!mCacheReadEnd
.IsNull() && mCacheReadEnd
< mResponseEnd
)) {
768 mResponseEnd
= mCacheReadEnd
;
770 if (mResponseEnd
.IsNull()) {
771 mResponseEnd
= mWorkerResponseEnd
;
773 // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
774 if (mResponseEnd
.IsNull()) {
775 return ResponseStartHighRes(aPerformance
);
777 DOMHighResTimeStamp rawValue
=
778 TimeStampToDOMHighRes(aPerformance
, mResponseEnd
);
779 return nsRFPService::ReduceTimePrecisionAsMSecs(
780 rawValue
, aPerformance
->GetRandomTimelineSeed(),
781 aPerformance
->GetRTPCallerType());
784 DOMTimeMilliSec
PerformanceTiming::ResponseEnd() {
785 return static_cast<int64_t>(mTimingData
->ResponseEndHighRes(mPerformance
));
788 JSObject
* PerformanceTiming::WrapObject(JSContext
* cx
,
789 JS::Handle
<JSObject
*> aGivenProto
) {
790 return PerformanceTiming_Binding::Wrap(cx
, this, aGivenProto
);
793 bool PerformanceTiming::IsTopLevelContentDocument() const {
794 nsCOMPtr
<Document
> document
= mPerformance
->GetDocumentIfCurrent();
799 if (BrowsingContext
* bc
= document
->GetBrowsingContext()) {
800 return bc
->IsTopContent();
805 nsTArray
<nsCOMPtr
<nsIServerTiming
>>
806 CacheablePerformanceTimingData::GetServerTiming() {
807 if (!StaticPrefs::dom_enable_performance() || !IsInitialized() ||
809 return nsTArray
<nsCOMPtr
<nsIServerTiming
>>();
812 return mServerTiming
.Clone();
815 } // namespace mozilla::dom