/src/mozilla-central/dom/performance/PerformanceTiming.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "PerformanceTiming.h" |
8 | | #include "mozilla/dom/PerformanceTimingBinding.h" |
9 | | #include "mozilla/Telemetry.h" |
10 | | #include "nsIDocShell.h" |
11 | | #include "nsIDocShellTreeItem.h" |
12 | | #include "nsIDocument.h" |
13 | | #include "nsITimedChannel.h" |
14 | | |
15 | | namespace mozilla { |
16 | | namespace dom { |
17 | | |
18 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance) |
19 | | |
20 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef) |
21 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release) |
22 | | |
23 | | /* static */ PerformanceTimingData* |
24 | | PerformanceTimingData::Create(nsITimedChannel* aTimedChannel, |
25 | | nsIHttpChannel* aChannel, |
26 | | DOMHighResTimeStamp aZeroTime, |
27 | | nsAString& aInitiatorType, |
28 | | nsAString& aEntryName) |
29 | 0 | { |
30 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
31 | 0 |
|
32 | 0 | // Check if resource timing is prefed off. |
33 | 0 | if (!nsContentUtils::IsResourceTimingEnabled()) { |
34 | 0 | return nullptr; |
35 | 0 | } |
36 | 0 | |
37 | 0 | if (!aChannel || !aTimedChannel) { |
38 | 0 | return nullptr; |
39 | 0 | } |
40 | 0 | |
41 | 0 | bool reportTiming = true; |
42 | 0 | aTimedChannel->GetReportResourceTiming(&reportTiming); |
43 | 0 |
|
44 | 0 | if (!reportTiming) { |
45 | 0 | return nullptr; |
46 | 0 | } |
47 | 0 | |
48 | 0 | aTimedChannel->GetInitiatorType(aInitiatorType); |
49 | 0 |
|
50 | 0 | // If the initiator type had no valid value, then set it to the default |
51 | 0 | // ("other") value. |
52 | 0 | if (aInitiatorType.IsEmpty()) { |
53 | 0 | aInitiatorType = NS_LITERAL_STRING("other"); |
54 | 0 | } |
55 | 0 |
|
56 | 0 | // According to the spec, "The name attribute must return the resolved URL |
57 | 0 | // of the requested resource. This attribute must not change even if the |
58 | 0 | // fetch redirected to a different URL." |
59 | 0 | nsCOMPtr<nsIURI> originalURI; |
60 | 0 | aChannel->GetOriginalURI(getter_AddRefs(originalURI)); |
61 | 0 |
|
62 | 0 | nsAutoCString name; |
63 | 0 | originalURI->GetSpec(name); |
64 | 0 | aEntryName = NS_ConvertUTF8toUTF16(name); |
65 | 0 |
|
66 | 0 | // The nsITimedChannel argument will be used to gather all the timings. |
67 | 0 | // The nsIHttpChannel argument will be used to check if any cross-origin |
68 | 0 | // redirects occurred. |
69 | 0 | // The last argument is the "zero time" (offset). Since we don't want |
70 | 0 | // any offset for the resource timing, this will be set to "0" - the |
71 | 0 | // resource timing returns a relative timing (no offset). |
72 | 0 | return new PerformanceTimingData(aTimedChannel, aChannel, 0); |
73 | 0 | } |
74 | | |
75 | | PerformanceTiming::PerformanceTiming(Performance* aPerformance, |
76 | | nsITimedChannel* aChannel, |
77 | | nsIHttpChannel* aHttpChannel, |
78 | | DOMHighResTimeStamp aZeroTime) |
79 | | : mPerformance(aPerformance) |
80 | 0 | { |
81 | 0 | MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); |
82 | 0 |
|
83 | 0 | mTimingData.reset(new PerformanceTimingData(aChannel, aHttpChannel, |
84 | 0 | aPerformance->IsSystemPrincipal() |
85 | 0 | ? aZeroTime |
86 | 0 | : nsRFPService::ReduceTimePrecisionAsMSecs(aZeroTime, |
87 | 0 | aPerformance->GetRandomTimelineSeed()))); |
88 | 0 |
|
89 | 0 | // Non-null aHttpChannel implies that this PerformanceTiming object is being |
90 | 0 | // used for subresources, which is irrelevant to this probe. |
91 | 0 | if (!aHttpChannel && |
92 | 0 | nsContentUtils::IsPerformanceTimingEnabled() && |
93 | 0 | IsTopLevelContentDocument()) { |
94 | 0 | Telemetry::Accumulate(Telemetry::TIME_TO_RESPONSE_START_MS, |
95 | 0 | mTimingData->ResponseStartHighRes(aPerformance) - |
96 | 0 | mTimingData->ZeroTime()); |
97 | 0 | } |
98 | 0 | } |
99 | | |
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) |
105 | | : mZeroTime(0.0) |
106 | | , mFetchStart(0.0) |
107 | | , mEncodedBodySize(0) |
108 | | , mTransferSize(0) |
109 | | , mDecodedBodySize(0) |
110 | | , mRedirectCount(0) |
111 | | , mAllRedirectsSameOrigin(true) |
112 | | , mReportCrossOriginRedirect(true) |
113 | | , mSecureConnection(false) |
114 | | , mTimingAllowed(true) |
115 | | , mInitialized(false) |
116 | 0 | { |
117 | 0 | mInitialized = !!aChannel; |
118 | 0 | mZeroTime = aZeroTime; |
119 | 0 |
|
120 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
121 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
122 | 0 | mZeroTime = 0; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | nsCOMPtr<nsIURI> uri; |
126 | 0 | if (aHttpChannel) { |
127 | 0 | aHttpChannel->GetURI(getter_AddRefs(uri)); |
128 | 0 | } else { |
129 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
130 | 0 | if (httpChannel) { |
131 | 0 | httpChannel->GetURI(getter_AddRefs(uri)); |
132 | 0 | } |
133 | 0 | } |
134 | 0 |
|
135 | 0 | if (uri) { |
136 | 0 | nsresult rv = uri->SchemeIs("https", &mSecureConnection); |
137 | 0 | if (NS_FAILED(rv)) { |
138 | 0 | mSecureConnection = false; |
139 | 0 | } |
140 | 0 | } |
141 | 0 |
|
142 | 0 | if (aChannel) { |
143 | 0 | aChannel->GetAsyncOpen(&mAsyncOpen); |
144 | 0 | aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin); |
145 | 0 | aChannel->GetRedirectCount(&mRedirectCount); |
146 | 0 | aChannel->GetRedirectStart(&mRedirectStart); |
147 | 0 | aChannel->GetRedirectEnd(&mRedirectEnd); |
148 | 0 | aChannel->GetDomainLookupStart(&mDomainLookupStart); |
149 | 0 | aChannel->GetDomainLookupEnd(&mDomainLookupEnd); |
150 | 0 | aChannel->GetConnectStart(&mConnectStart); |
151 | 0 | aChannel->GetSecureConnectionStart(&mSecureConnectionStart); |
152 | 0 | aChannel->GetConnectEnd(&mConnectEnd); |
153 | 0 | aChannel->GetRequestStart(&mRequestStart); |
154 | 0 | aChannel->GetResponseStart(&mResponseStart); |
155 | 0 | aChannel->GetCacheReadStart(&mCacheReadStart); |
156 | 0 | aChannel->GetResponseEnd(&mResponseEnd); |
157 | 0 | aChannel->GetCacheReadEnd(&mCacheReadEnd); |
158 | 0 |
|
159 | 0 | aChannel->GetDispatchFetchEventStart(&mWorkerStart); |
160 | 0 | aChannel->GetHandleFetchEventStart(&mWorkerRequestStart); |
161 | 0 | // TODO: Track when FetchEvent.respondWith() promise resolves as |
162 | 0 | // ServiceWorker interception responseStart? |
163 | 0 | aChannel->GetHandleFetchEventEnd(&mWorkerResponseEnd); |
164 | 0 |
|
165 | 0 | aChannel->GetNativeServerTiming(mServerTiming); |
166 | 0 |
|
167 | 0 | // The performance timing api essentially requires that the event timestamps |
168 | 0 | // have a strict relation with each other. The truth, however, is the |
169 | 0 | // browser engages in a number of speculative activities that sometimes mean |
170 | 0 | // connections and lookups begin at different times. Workaround that here by |
171 | 0 | // clamping these values to what we expect FetchStart to be. This means the |
172 | 0 | // later of AsyncOpen or WorkerStart times. |
173 | 0 | if (!mAsyncOpen.IsNull()) { |
174 | 0 | // We want to clamp to the expected FetchStart value. This is later of |
175 | 0 | // the AsyncOpen and WorkerStart values. |
176 | 0 | const TimeStamp* clampTime = &mAsyncOpen; |
177 | 0 | if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) { |
178 | 0 | clampTime = &mWorkerStart; |
179 | 0 | } |
180 | 0 |
|
181 | 0 | if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) { |
182 | 0 | mDomainLookupStart = *clampTime; |
183 | 0 | } |
184 | 0 |
|
185 | 0 | if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) { |
186 | 0 | mDomainLookupEnd = *clampTime; |
187 | 0 | } |
188 | 0 |
|
189 | 0 | if (!mConnectStart.IsNull() && mConnectStart < *clampTime) { |
190 | 0 | mConnectStart = *clampTime; |
191 | 0 | } |
192 | 0 |
|
193 | 0 | if (mSecureConnection && !mSecureConnectionStart.IsNull() && |
194 | 0 | mSecureConnectionStart < *clampTime) { |
195 | 0 | mSecureConnectionStart = *clampTime; |
196 | 0 | } |
197 | 0 |
|
198 | 0 | if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) { |
199 | 0 | mConnectEnd = *clampTime; |
200 | 0 | } |
201 | 0 | } |
202 | 0 | } |
203 | 0 |
|
204 | 0 | // The aHttpChannel argument is null if this PerformanceTiming object is |
205 | 0 | // being used for navigation timing (which is only relevant for documents). |
206 | 0 | // It has a non-null value if this PerformanceTiming object is being used |
207 | 0 | // for resource timing, which can include document loads, both toplevel and |
208 | 0 | // in subframes, and resources linked from a document. |
209 | 0 | if (aHttpChannel) { |
210 | 0 | SetPropertiesFromHttpChannel(aHttpChannel, aChannel); |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | void |
215 | | PerformanceTimingData::SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel, |
216 | | nsITimedChannel* aChannel) |
217 | 0 | { |
218 | 0 | MOZ_ASSERT(aHttpChannel); |
219 | 0 |
|
220 | 0 | nsAutoCString protocol; |
221 | 0 | Unused << aHttpChannel->GetProtocolVersion(protocol); |
222 | 0 | mNextHopProtocol = NS_ConvertUTF8toUTF16(protocol); |
223 | 0 |
|
224 | 0 | Unused << aHttpChannel->GetEncodedBodySize(&mEncodedBodySize); |
225 | 0 | Unused << aHttpChannel->GetTransferSize(&mTransferSize); |
226 | 0 | Unused << aHttpChannel->GetDecodedBodySize(&mDecodedBodySize); |
227 | 0 | if (mDecodedBodySize == 0) { |
228 | 0 | mDecodedBodySize = mEncodedBodySize; |
229 | 0 | } |
230 | 0 |
|
231 | 0 | mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel); |
232 | 0 | bool redirectsPassCheck = false; |
233 | 0 | aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck); |
234 | 0 | mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck; |
235 | 0 | } |
236 | | |
237 | | PerformanceTiming::~PerformanceTiming() |
238 | 0 | { |
239 | 0 | } |
240 | | |
241 | | DOMHighResTimeStamp |
242 | | PerformanceTimingData::FetchStartHighRes(Performance* aPerformance) |
243 | 0 | { |
244 | 0 | MOZ_ASSERT(aPerformance); |
245 | 0 |
|
246 | 0 | if (!mFetchStart) { |
247 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
248 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
249 | 0 | return mZeroTime; |
250 | 0 | } |
251 | 0 | MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be " |
252 | 0 | "valid if the performance timing is enabled"); |
253 | 0 | if (!mAsyncOpen.IsNull()) { |
254 | 0 | if (!mWorkerRequestStart.IsNull() && mWorkerRequestStart > mAsyncOpen) { |
255 | 0 | mFetchStart = TimeStampToDOMHighRes(aPerformance, mWorkerRequestStart); |
256 | 0 | } else { |
257 | 0 | mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } |
261 | 0 | if (aPerformance->IsSystemPrincipal()) { |
262 | 0 | return mFetchStart; |
263 | 0 | } |
264 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(mFetchStart, |
265 | 0 | aPerformance->GetRandomTimelineSeed()); |
266 | 0 | } |
267 | | |
268 | | DOMTimeMilliSec |
269 | | PerformanceTiming::FetchStart() |
270 | 0 | { |
271 | 0 | return static_cast<int64_t>(mTimingData->FetchStartHighRes(mPerformance)); |
272 | 0 | } |
273 | | |
274 | | bool |
275 | | PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, |
276 | | nsITimedChannel* aChannel) |
277 | 0 | { |
278 | 0 | if (!IsInitialized()) { |
279 | 0 | return false; |
280 | 0 | } |
281 | 0 | |
282 | 0 | // Check that the current document passes the ckeck. |
283 | 0 | nsCOMPtr<nsILoadInfo> loadInfo; |
284 | 0 | aResourceChannel->GetLoadInfo(getter_AddRefs(loadInfo)); |
285 | 0 | if (!loadInfo) { |
286 | 0 | return false; |
287 | 0 | } |
288 | 0 | |
289 | 0 | // TYPE_DOCUMENT loads have no loadingPrincipal. |
290 | 0 | if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) { |
291 | 0 | return true; |
292 | 0 | } |
293 | 0 | |
294 | 0 | nsCOMPtr<nsIPrincipal> principal = loadInfo->LoadingPrincipal(); |
295 | 0 |
|
296 | 0 | // Check if the resource is either same origin as the page that started |
297 | 0 | // the load, or if the response contains the proper Timing-Allow-Origin |
298 | 0 | // header with the domain of the page that started the load. |
299 | 0 | return aChannel->TimingAllowCheck(principal); |
300 | 0 | } |
301 | | |
302 | | uint8_t |
303 | | PerformanceTimingData::GetRedirectCount() const |
304 | 0 | { |
305 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
306 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
307 | 0 | return 0; |
308 | 0 | } |
309 | 0 | if (!mAllRedirectsSameOrigin) { |
310 | 0 | return 0; |
311 | 0 | } |
312 | 0 | return mRedirectCount; |
313 | 0 | } |
314 | | |
315 | | bool |
316 | | PerformanceTimingData::ShouldReportCrossOriginRedirect() const |
317 | 0 | { |
318 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
319 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
320 | 0 | return false; |
321 | 0 | } |
322 | 0 | |
323 | 0 | // If the redirect count is 0, or if one of the cross-origin |
324 | 0 | // redirects doesn't have the proper Timing-Allow-Origin header, |
325 | 0 | // then RedirectStart and RedirectEnd will be set to zero |
326 | 0 | return (mRedirectCount != 0) && mReportCrossOriginRedirect; |
327 | 0 | } |
328 | | |
329 | | DOMHighResTimeStamp |
330 | | PerformanceTimingData::AsyncOpenHighRes(Performance* aPerformance) |
331 | 0 | { |
332 | 0 | MOZ_ASSERT(aPerformance); |
333 | 0 |
|
334 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
335 | 0 | nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) { |
336 | 0 | return mZeroTime; |
337 | 0 | } |
338 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mAsyncOpen); |
339 | 0 | if (aPerformance->IsSystemPrincipal()) { |
340 | 0 | return rawValue; |
341 | 0 | } |
342 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
343 | 0 | aPerformance->GetRandomTimelineSeed()); |
344 | 0 | } |
345 | | |
346 | | DOMHighResTimeStamp |
347 | | PerformanceTimingData::WorkerStartHighRes(Performance* aPerformance) |
348 | 0 | { |
349 | 0 | MOZ_ASSERT(aPerformance); |
350 | 0 |
|
351 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
352 | 0 | nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) { |
353 | 0 | return mZeroTime; |
354 | 0 | } |
355 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mWorkerStart); |
356 | 0 | if (aPerformance->IsSystemPrincipal()) { |
357 | 0 | return rawValue; |
358 | 0 | } |
359 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
360 | 0 | aPerformance->GetRandomTimelineSeed()); |
361 | 0 | } |
362 | | |
363 | | /** |
364 | | * RedirectStartHighRes() is used by both the navigation timing and the |
365 | | * resource timing. Since, navigation timing and resource timing check and |
366 | | * interpret cross-domain redirects in a different manner, |
367 | | * RedirectStartHighRes() will make no checks for cross-domain redirect. |
368 | | * It's up to the consumers of this method (PerformanceTiming::RedirectStart() |
369 | | * and PerformanceResourceTiming::RedirectStart() to make such verifications. |
370 | | * |
371 | | * @return a valid timing if the Performance Timing is enabled |
372 | | */ |
373 | | DOMHighResTimeStamp |
374 | | PerformanceTimingData::RedirectStartHighRes(Performance* aPerformance) |
375 | 0 | { |
376 | 0 | MOZ_ASSERT(aPerformance); |
377 | 0 |
|
378 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
379 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
380 | 0 | return mZeroTime; |
381 | 0 | } |
382 | 0 | return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectStart); |
383 | 0 | } |
384 | | |
385 | | DOMTimeMilliSec |
386 | | PerformanceTiming::RedirectStart() |
387 | 0 | { |
388 | 0 | if (!mTimingData->IsInitialized()) { |
389 | 0 | return 0; |
390 | 0 | } |
391 | 0 | // We have to check if all the redirect URIs had the same origin (since there |
392 | 0 | // is no check in RedirectStartHighRes()) |
393 | 0 | if (mTimingData->AllRedirectsSameOrigin() && |
394 | 0 | mTimingData->RedirectCountReal()) { |
395 | 0 | return static_cast<int64_t>(mTimingData->RedirectStartHighRes(mPerformance)); |
396 | 0 | } |
397 | 0 | return 0; |
398 | 0 | } |
399 | | |
400 | | /** |
401 | | * RedirectEndHighRes() is used by both the navigation timing and the resource |
402 | | * timing. Since, navigation timing and resource timing check and interpret |
403 | | * cross-domain redirects in a different manner, RedirectEndHighRes() will make |
404 | | * no checks for cross-domain redirect. It's up to the consumers of this method |
405 | | * (PerformanceTiming::RedirectEnd() and |
406 | | * PerformanceResourceTiming::RedirectEnd() to make such verifications. |
407 | | * |
408 | | * @return a valid timing if the Performance Timing is enabled |
409 | | */ |
410 | | DOMHighResTimeStamp |
411 | | PerformanceTimingData::RedirectEndHighRes(Performance* aPerformance) |
412 | 0 | { |
413 | 0 | MOZ_ASSERT(aPerformance); |
414 | 0 |
|
415 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
416 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
417 | 0 | return mZeroTime; |
418 | 0 | } |
419 | 0 | return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectEnd); |
420 | 0 | } |
421 | | |
422 | | DOMTimeMilliSec |
423 | | PerformanceTiming::RedirectEnd() |
424 | 0 | { |
425 | 0 | if (!mTimingData->IsInitialized()) { |
426 | 0 | return 0; |
427 | 0 | } |
428 | 0 | // We have to check if all the redirect URIs had the same origin (since there |
429 | 0 | // is no check in RedirectEndHighRes()) |
430 | 0 | if (mTimingData->AllRedirectsSameOrigin() && |
431 | 0 | mTimingData->RedirectCountReal()) { |
432 | 0 | return static_cast<int64_t>(mTimingData->RedirectEndHighRes(mPerformance)); |
433 | 0 | } |
434 | 0 | return 0; |
435 | 0 | } |
436 | | |
437 | | DOMHighResTimeStamp |
438 | | PerformanceTimingData::DomainLookupStartHighRes(Performance* aPerformance) |
439 | 0 | { |
440 | 0 | MOZ_ASSERT(aPerformance); |
441 | 0 |
|
442 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
443 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
444 | 0 | return mZeroTime; |
445 | 0 | } |
446 | 0 | return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, |
447 | 0 | mDomainLookupStart); |
448 | 0 | } |
449 | | |
450 | | DOMTimeMilliSec |
451 | | PerformanceTiming::DomainLookupStart() |
452 | 0 | { |
453 | 0 | return static_cast<int64_t>(mTimingData->DomainLookupStartHighRes(mPerformance)); |
454 | 0 | } |
455 | | |
456 | | DOMHighResTimeStamp |
457 | | PerformanceTimingData::DomainLookupEndHighRes(Performance* aPerformance) |
458 | 0 | { |
459 | 0 | MOZ_ASSERT(aPerformance); |
460 | 0 |
|
461 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
462 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
463 | 0 | return mZeroTime; |
464 | 0 | } |
465 | 0 | // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null |
466 | 0 | if (mDomainLookupEnd.IsNull()) { |
467 | 0 | return DomainLookupStartHighRes(aPerformance); |
468 | 0 | } |
469 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd); |
470 | 0 | if (aPerformance->IsSystemPrincipal()) { |
471 | 0 | return rawValue; |
472 | 0 | } |
473 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
474 | 0 | aPerformance->GetRandomTimelineSeed()); |
475 | 0 | } |
476 | | |
477 | | DOMTimeMilliSec |
478 | | PerformanceTiming::DomainLookupEnd() |
479 | 0 | { |
480 | 0 | return static_cast<int64_t>(mTimingData->DomainLookupEndHighRes(mPerformance)); |
481 | 0 | } |
482 | | |
483 | | DOMHighResTimeStamp |
484 | | PerformanceTimingData::ConnectStartHighRes(Performance* aPerformance) |
485 | 0 | { |
486 | 0 | MOZ_ASSERT(aPerformance); |
487 | 0 |
|
488 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
489 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
490 | 0 | return mZeroTime; |
491 | 0 | } |
492 | 0 | if (mConnectStart.IsNull()) { |
493 | 0 | return DomainLookupEndHighRes(aPerformance); |
494 | 0 | } |
495 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mConnectStart); |
496 | 0 | if (aPerformance->IsSystemPrincipal()) { |
497 | 0 | return rawValue; |
498 | 0 | } |
499 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
500 | 0 | aPerformance->GetRandomTimelineSeed()); |
501 | 0 | } |
502 | | |
503 | | DOMTimeMilliSec |
504 | | PerformanceTiming::ConnectStart() |
505 | 0 | { |
506 | 0 | return static_cast<int64_t>(mTimingData->ConnectStartHighRes(mPerformance)); |
507 | 0 | } |
508 | | |
509 | | DOMHighResTimeStamp |
510 | | PerformanceTimingData::SecureConnectionStartHighRes(Performance* aPerformance) |
511 | 0 | { |
512 | 0 | MOZ_ASSERT(aPerformance); |
513 | 0 |
|
514 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
515 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
516 | 0 | return mZeroTime; |
517 | 0 | } |
518 | 0 | if (!mSecureConnection) { |
519 | 0 | return 0; // We use 0 here, because mZeroTime is sometimes set to the navigation |
520 | 0 | // start time. |
521 | 0 | } |
522 | 0 | if (mSecureConnectionStart.IsNull()) { |
523 | 0 | return mZeroTime; |
524 | 0 | } |
525 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart); |
526 | 0 | if (aPerformance->IsSystemPrincipal()) { |
527 | 0 | return rawValue; |
528 | 0 | } |
529 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
530 | 0 | aPerformance->GetRandomTimelineSeed()); |
531 | 0 | } |
532 | | |
533 | | DOMTimeMilliSec |
534 | | PerformanceTiming::SecureConnectionStart() |
535 | 0 | { |
536 | 0 | return static_cast<int64_t>(mTimingData->SecureConnectionStartHighRes(mPerformance)); |
537 | 0 | } |
538 | | |
539 | | DOMHighResTimeStamp |
540 | | PerformanceTimingData::ConnectEndHighRes(Performance* aPerformance) |
541 | 0 | { |
542 | 0 | MOZ_ASSERT(aPerformance); |
543 | 0 |
|
544 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
545 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
546 | 0 | return mZeroTime; |
547 | 0 | } |
548 | 0 | // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null |
549 | 0 | if (mConnectEnd.IsNull()) { |
550 | 0 | return ConnectStartHighRes(aPerformance); |
551 | 0 | } |
552 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mConnectEnd); |
553 | 0 | if (aPerformance->IsSystemPrincipal()) { |
554 | 0 | return rawValue; |
555 | 0 | } |
556 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
557 | 0 | aPerformance->GetRandomTimelineSeed()); |
558 | 0 | } |
559 | | |
560 | | DOMTimeMilliSec |
561 | | PerformanceTiming::ConnectEnd() |
562 | 0 | { |
563 | 0 | return static_cast<int64_t>(mTimingData->ConnectEndHighRes(mPerformance)); |
564 | 0 | } |
565 | | |
566 | | DOMHighResTimeStamp |
567 | | PerformanceTimingData::RequestStartHighRes(Performance* aPerformance) |
568 | 0 | { |
569 | 0 | MOZ_ASSERT(aPerformance); |
570 | 0 |
|
571 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
572 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
573 | 0 | return mZeroTime; |
574 | 0 | } |
575 | 0 | |
576 | 0 | if (mRequestStart.IsNull()) { |
577 | 0 | mRequestStart = mWorkerRequestStart; |
578 | 0 | } |
579 | 0 |
|
580 | 0 | return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRequestStart); |
581 | 0 | } |
582 | | |
583 | | DOMTimeMilliSec |
584 | | PerformanceTiming::RequestStart() |
585 | 0 | { |
586 | 0 | return static_cast<int64_t>(mTimingData->RequestStartHighRes(mPerformance)); |
587 | 0 | } |
588 | | |
589 | | DOMHighResTimeStamp |
590 | | PerformanceTimingData::ResponseStartHighRes(Performance* aPerformance) |
591 | 0 | { |
592 | 0 | MOZ_ASSERT(aPerformance); |
593 | 0 |
|
594 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
595 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
596 | 0 | return mZeroTime; |
597 | 0 | } |
598 | 0 | if (mResponseStart.IsNull() || |
599 | 0 | (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) { |
600 | 0 | mResponseStart = mCacheReadStart; |
601 | 0 | } |
602 | 0 |
|
603 | 0 | if (mResponseStart.IsNull() || |
604 | 0 | (!mRequestStart.IsNull() && mResponseStart < mRequestStart)) { |
605 | 0 | mResponseStart = mRequestStart; |
606 | 0 | } |
607 | 0 | return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mResponseStart); |
608 | 0 | } |
609 | | |
610 | | DOMTimeMilliSec |
611 | | PerformanceTiming::ResponseStart() |
612 | 0 | { |
613 | 0 | return static_cast<int64_t>(mTimingData->ResponseStartHighRes(mPerformance)); |
614 | 0 | } |
615 | | |
616 | | DOMHighResTimeStamp |
617 | | PerformanceTimingData::ResponseEndHighRes(Performance* aPerformance) |
618 | 0 | { |
619 | 0 | MOZ_ASSERT(aPerformance); |
620 | 0 |
|
621 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
622 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
623 | 0 | return mZeroTime; |
624 | 0 | } |
625 | 0 | if (mResponseEnd.IsNull() || |
626 | 0 | (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) { |
627 | 0 | mResponseEnd = mCacheReadEnd; |
628 | 0 | } |
629 | 0 | if (mResponseEnd.IsNull()) { |
630 | 0 | mResponseEnd = mWorkerResponseEnd; |
631 | 0 | } |
632 | 0 | // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null |
633 | 0 | if (mResponseEnd.IsNull()) { |
634 | 0 | return ResponseStartHighRes(aPerformance); |
635 | 0 | } |
636 | 0 | DOMHighResTimeStamp rawValue = TimeStampToDOMHighRes(aPerformance, mResponseEnd); |
637 | 0 | if (aPerformance->IsSystemPrincipal()) { |
638 | 0 | return rawValue; |
639 | 0 | } |
640 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawValue, |
641 | 0 | aPerformance->GetRandomTimelineSeed()); |
642 | 0 | } |
643 | | |
644 | | DOMTimeMilliSec |
645 | | PerformanceTiming::ResponseEnd() |
646 | 0 | { |
647 | 0 | return static_cast<int64_t>(mTimingData->ResponseEndHighRes(mPerformance)); |
648 | 0 | } |
649 | | |
650 | | JSObject* |
651 | | PerformanceTiming::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) |
652 | 0 | { |
653 | 0 | return PerformanceTiming_Binding::Wrap(cx, this, aGivenProto); |
654 | 0 | } |
655 | | |
656 | | bool |
657 | | PerformanceTiming::IsTopLevelContentDocument() const |
658 | 0 | { |
659 | 0 | nsCOMPtr<nsIDocument> document = mPerformance->GetDocumentIfCurrent(); |
660 | 0 | if (!document) { |
661 | 0 | return false; |
662 | 0 | } |
663 | 0 | nsCOMPtr<nsIDocShell> docShell = document->GetDocShell(); |
664 | 0 | if (!docShell) { |
665 | 0 | return false; |
666 | 0 | } |
667 | 0 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
668 | 0 | Unused << docShell->GetSameTypeRootTreeItem(getter_AddRefs(rootItem)); |
669 | 0 | if (rootItem.get() != static_cast<nsIDocShellTreeItem*>(docShell.get())) { |
670 | 0 | return false; |
671 | 0 | } |
672 | 0 | return rootItem->ItemType() == nsIDocShellTreeItem::typeContent; |
673 | 0 | } |
674 | | |
675 | | nsTArray<nsCOMPtr<nsIServerTiming>> |
676 | | PerformanceTimingData::GetServerTiming() |
677 | 0 | { |
678 | 0 | if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || |
679 | 0 | !TimingAllowed() || |
680 | 0 | nsContentUtils::ShouldResistFingerprinting()) { |
681 | 0 | return nsTArray<nsCOMPtr<nsIServerTiming>>(); |
682 | 0 | } |
683 | 0 | |
684 | 0 | return nsTArray<nsCOMPtr<nsIServerTiming>>(mServerTiming); |
685 | 0 | } |
686 | | |
687 | | } // dom namespace |
688 | | } // mozilla namespace |