/src/mozilla-central/dom/performance/PerformanceTiming.h
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 | | #ifndef mozilla_dom_PerformanceTiming_h |
8 | | #define mozilla_dom_PerformanceTiming_h |
9 | | |
10 | | #include "mozilla/Attributes.h" |
11 | | #include "nsContentUtils.h" |
12 | | #include "nsDOMNavigationTiming.h" |
13 | | #include "nsRFPService.h" |
14 | | #include "nsWrapperCache.h" |
15 | | #include "Performance.h" |
16 | | #include "nsITimedChannel.h" |
17 | | |
18 | | class nsIHttpChannel; |
19 | | |
20 | | namespace mozilla { |
21 | | namespace dom { |
22 | | |
23 | | class PerformanceTiming; |
24 | | |
25 | | class PerformanceTimingData final |
26 | | { |
27 | | friend class PerformanceTiming; |
28 | | |
29 | | public: |
30 | | // This can return null. |
31 | | static PerformanceTimingData* |
32 | | Create(nsITimedChannel* aChannel, |
33 | | nsIHttpChannel* aHttpChannel, |
34 | | DOMHighResTimeStamp aZeroTime, |
35 | | nsAString& aInitiatorType, |
36 | | nsAString& aEntryName); |
37 | | |
38 | | PerformanceTimingData(nsITimedChannel* aChannel, |
39 | | nsIHttpChannel* aHttpChannel, |
40 | | DOMHighResTimeStamp aZeroTime); |
41 | | |
42 | | void |
43 | | SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel, |
44 | | nsITimedChannel* aChannel); |
45 | | |
46 | | bool IsInitialized() const |
47 | 0 | { |
48 | 0 | return mInitialized; |
49 | 0 | } |
50 | | |
51 | | const nsString& NextHopProtocol() const |
52 | | { |
53 | | return mNextHopProtocol; |
54 | | } |
55 | | |
56 | | uint64_t TransferSize() const |
57 | | { |
58 | | return mTransferSize; |
59 | | } |
60 | | |
61 | | uint64_t EncodedBodySize() const |
62 | | { |
63 | | return mEncodedBodySize; |
64 | | } |
65 | | |
66 | | uint64_t DecodedBodySize() const |
67 | | { |
68 | | return mDecodedBodySize; |
69 | | } |
70 | | |
71 | | /** |
72 | | * @param aStamp |
73 | | * The TimeStamp recorded for a specific event. This TimeStamp can |
74 | | * be null. |
75 | | * @return the duration of an event with a given TimeStamp, relative to the |
76 | | * navigationStart TimeStamp (the moment the user landed on the |
77 | | * page), if the given TimeStamp is valid. Otherwise, it will return |
78 | | * the FetchStart timing value. |
79 | | */ |
80 | | inline DOMHighResTimeStamp |
81 | | TimeStampToReducedDOMHighResOrFetchStart(Performance* aPerformance, |
82 | | TimeStamp aStamp) |
83 | 0 | { |
84 | 0 | MOZ_ASSERT(aPerformance); |
85 | 0 |
|
86 | 0 | if(aStamp.IsNull()) { |
87 | 0 | return FetchStartHighRes(aPerformance); |
88 | 0 | } |
89 | 0 | |
90 | 0 | DOMHighResTimeStamp rawTimestamp = TimeStampToDOMHighRes(aPerformance, aStamp); |
91 | 0 | if (aPerformance->IsSystemPrincipal()) { |
92 | 0 | return rawTimestamp; |
93 | 0 | } |
94 | 0 | |
95 | 0 | return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimestamp, |
96 | 0 | aPerformance->GetRandomTimelineSeed()); |
97 | 0 | } |
98 | | |
99 | | /** |
100 | | * The nsITimedChannel records an absolute timestamp for each event. |
101 | | * The nsDOMNavigationTiming will record the moment when the user landed on |
102 | | * the page. This is a window.performance unique timestamp, so it can be used |
103 | | * for all the events (navigation timing and resource timing events). |
104 | | * |
105 | | * The algorithm operates in 2 steps: |
106 | | * 1. The first step is to subtract the two timestamps: the argument (the |
107 | | * event's timestamp) and the navigation start timestamp. This will result in |
108 | | * a relative timestamp of the event (relative to the navigation start - |
109 | | * window.performance.timing.navigationStart). |
110 | | * 2. The second step is to add any required offset (the mZeroTime). For now, |
111 | | * this offset value is either 0 (for the resource timing), or equal to |
112 | | * "performance.navigationStart" (for navigation timing). |
113 | | * For the resource timing, mZeroTime is set to 0, causing the result to be a |
114 | | * relative time. |
115 | | * For the navigation timing, mZeroTime is set to "performance.navigationStart" |
116 | | * causing the result be an absolute time. |
117 | | * |
118 | | * @param aStamp |
119 | | * The TimeStamp recorded for a specific event. This TimeStamp can't |
120 | | * be null. |
121 | | * @return number of milliseconds value as one of: |
122 | | * - relative to the navigation start time, time the user has landed on the |
123 | | * page |
124 | | * - an absolute wall clock time since the unix epoch |
125 | | */ |
126 | | inline DOMHighResTimeStamp |
127 | | TimeStampToDOMHighRes(Performance* aPerformance, TimeStamp aStamp) const |
128 | 0 | { |
129 | 0 | MOZ_ASSERT(aPerformance); |
130 | 0 | MOZ_ASSERT(!aStamp.IsNull()); |
131 | 0 |
|
132 | 0 | TimeDuration duration = aStamp - aPerformance->CreationTimeStamp(); |
133 | 0 | return duration.ToMilliseconds() + mZeroTime; |
134 | 0 | } |
135 | | |
136 | | // The last channel's AsyncOpen time. This may occur before the FetchStart |
137 | | // in some cases. |
138 | | DOMHighResTimeStamp AsyncOpenHighRes(Performance* aPerformance); |
139 | | |
140 | | // High resolution (used by resource timing) |
141 | | DOMHighResTimeStamp WorkerStartHighRes(Performance* aPerformance); |
142 | | DOMHighResTimeStamp FetchStartHighRes(Performance* aPerformance); |
143 | | DOMHighResTimeStamp RedirectStartHighRes(Performance* aPerformance); |
144 | | DOMHighResTimeStamp RedirectEndHighRes(Performance* aPerformance); |
145 | | DOMHighResTimeStamp DomainLookupStartHighRes(Performance* aPerformance); |
146 | | DOMHighResTimeStamp DomainLookupEndHighRes(Performance* aPerformance); |
147 | | DOMHighResTimeStamp ConnectStartHighRes(Performance* aPerformance); |
148 | | DOMHighResTimeStamp SecureConnectionStartHighRes(Performance* aPerformance); |
149 | | DOMHighResTimeStamp ConnectEndHighRes(Performance* aPerformance); |
150 | | DOMHighResTimeStamp RequestStartHighRes(Performance* aPerformance); |
151 | | DOMHighResTimeStamp ResponseStartHighRes(Performance* aPerformance); |
152 | | DOMHighResTimeStamp ResponseEndHighRes(Performance* aPerformance); |
153 | | |
154 | 0 | DOMHighResTimeStamp ZeroTime() const { return mZeroTime; } |
155 | | |
156 | 0 | uint8_t RedirectCountReal() const { return mRedirectCount; } |
157 | | uint8_t GetRedirectCount() const; |
158 | | |
159 | 0 | bool AllRedirectsSameOrigin() const { return mAllRedirectsSameOrigin; } |
160 | | |
161 | | // If this is false the values of redirectStart/End will be 0 This is false if |
162 | | // no redirects occured, or if any of the responses failed the |
163 | | // timing-allow-origin check in HttpBaseChannel::TimingAllowCheck |
164 | | bool ShouldReportCrossOriginRedirect() const; |
165 | | |
166 | | // Cached result of CheckAllowedOrigin. If false, security sensitive |
167 | | // attributes of the resourceTiming object will be set to 0 |
168 | | bool TimingAllowed() const |
169 | 0 | { |
170 | 0 | return mTimingAllowed; |
171 | 0 | } |
172 | | |
173 | | nsTArray<nsCOMPtr<nsIServerTiming>> GetServerTiming(); |
174 | | |
175 | | private: |
176 | | // Checks if the resource is either same origin as the page that started |
177 | | // the load, or if the response contains the Timing-Allow-Origin header |
178 | | // with a value of * or matching the domain of the loading Principal |
179 | | bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, |
180 | | nsITimedChannel* aChannel); |
181 | | |
182 | | nsTArray<nsCOMPtr<nsIServerTiming>> mServerTiming; |
183 | | nsString mNextHopProtocol; |
184 | | |
185 | | TimeStamp mAsyncOpen; |
186 | | TimeStamp mRedirectStart; |
187 | | TimeStamp mRedirectEnd; |
188 | | TimeStamp mDomainLookupStart; |
189 | | TimeStamp mDomainLookupEnd; |
190 | | TimeStamp mConnectStart; |
191 | | TimeStamp mSecureConnectionStart; |
192 | | TimeStamp mConnectEnd; |
193 | | TimeStamp mRequestStart; |
194 | | TimeStamp mResponseStart; |
195 | | TimeStamp mCacheReadStart; |
196 | | TimeStamp mResponseEnd; |
197 | | TimeStamp mCacheReadEnd; |
198 | | |
199 | | // ServiceWorker interception timing information |
200 | | TimeStamp mWorkerStart; |
201 | | TimeStamp mWorkerRequestStart; |
202 | | TimeStamp mWorkerResponseEnd; |
203 | | |
204 | | // This is an offset that will be added to each timing ([ms] resolution). |
205 | | // There are only 2 possible values: (1) logicaly equal to navigationStart |
206 | | // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results |
207 | | // are relative to the navigation start). |
208 | | DOMHighResTimeStamp mZeroTime; |
209 | | |
210 | | DOMHighResTimeStamp mFetchStart; |
211 | | |
212 | | uint64_t mEncodedBodySize; |
213 | | uint64_t mTransferSize; |
214 | | uint64_t mDecodedBodySize; |
215 | | |
216 | | uint8_t mRedirectCount; |
217 | | |
218 | | bool mAllRedirectsSameOrigin; |
219 | | |
220 | | // If the resourceTiming object should have non-zero redirectStart and |
221 | | // redirectEnd attributes. It is false if there were no redirects, or if any |
222 | | // of the responses didn't pass the timing-allow-check |
223 | | bool mReportCrossOriginRedirect; |
224 | | |
225 | | bool mSecureConnection; |
226 | | |
227 | | bool mTimingAllowed; |
228 | | |
229 | | bool mInitialized; |
230 | | }; |
231 | | |
232 | | // Script "performance.timing" object |
233 | | class PerformanceTiming final : public nsWrapperCache |
234 | | { |
235 | | public: |
236 | | /** |
237 | | * @param aPerformance |
238 | | * The performance object (the JS parent). |
239 | | * This will allow access to "window.performance.timing" attribute for |
240 | | * the navigation timing (can't be null). |
241 | | * @param aChannel |
242 | | * An nsITimedChannel used to gather all the networking timings by both |
243 | | * the navigation timing and the resource timing (can't be null). |
244 | | * @param aHttpChannel |
245 | | * An nsIHttpChannel (the resource's http channel). |
246 | | * This will be used by the resource timing cross-domain check |
247 | | * algorithm. |
248 | | * Argument is null for the navigation timing (navigation timing uses |
249 | | * another algorithm for the cross-domain redirects). |
250 | | * @param aZeroTime |
251 | | * The offset that will be added to the timestamp of each event. This |
252 | | * argument should be equal to performance.navigationStart for |
253 | | * navigation timing and "0" for the resource timing. |
254 | | */ |
255 | | PerformanceTiming(Performance* aPerformance, |
256 | | nsITimedChannel* aChannel, |
257 | | nsIHttpChannel* aHttpChannel, |
258 | | DOMHighResTimeStamp aZeroTime); |
259 | | NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PerformanceTiming) |
260 | | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PerformanceTiming) |
261 | | |
262 | | nsDOMNavigationTiming* GetDOMTiming() const |
263 | | { |
264 | | return mPerformance->GetDOMTiming(); |
265 | | } |
266 | | |
267 | | Performance* GetParentObject() const |
268 | | { |
269 | | return mPerformance; |
270 | | } |
271 | | |
272 | | virtual JSObject* WrapObject(JSContext *cx, |
273 | | JS::Handle<JSObject*> aGivenProto) override; |
274 | | |
275 | | // PerformanceNavigation WebIDL methods |
276 | | DOMTimeMilliSec NavigationStart() const |
277 | | { |
278 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
279 | | nsContentUtils::ShouldResistFingerprinting()) { |
280 | | return 0; |
281 | | } |
282 | | if (mPerformance->IsSystemPrincipal()) { |
283 | | return GetDOMTiming()->GetNavigationStart(); |
284 | | } |
285 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
286 | | GetDOMTiming()->GetNavigationStart(), |
287 | | mPerformance->GetRandomTimelineSeed()); |
288 | | } |
289 | | |
290 | | DOMTimeMilliSec UnloadEventStart() |
291 | | { |
292 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
293 | | nsContentUtils::ShouldResistFingerprinting()) { |
294 | | return 0; |
295 | | } |
296 | | if (mPerformance->IsSystemPrincipal()) { |
297 | | return GetDOMTiming()->GetUnloadEventStart(); |
298 | | } |
299 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
300 | | GetDOMTiming()->GetUnloadEventStart(), |
301 | | mPerformance->GetRandomTimelineSeed()); |
302 | | } |
303 | | |
304 | | DOMTimeMilliSec UnloadEventEnd() |
305 | | { |
306 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
307 | | nsContentUtils::ShouldResistFingerprinting()) { |
308 | | return 0; |
309 | | } |
310 | | if (mPerformance->IsSystemPrincipal()) { |
311 | | return GetDOMTiming()->GetUnloadEventEnd(); |
312 | | } |
313 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
314 | | GetDOMTiming()->GetUnloadEventEnd(), |
315 | | mPerformance->GetRandomTimelineSeed()); |
316 | | } |
317 | | |
318 | | // Low resolution (used by navigation timing) |
319 | | DOMTimeMilliSec FetchStart(); |
320 | | DOMTimeMilliSec RedirectStart(); |
321 | | DOMTimeMilliSec RedirectEnd(); |
322 | | DOMTimeMilliSec DomainLookupStart(); |
323 | | DOMTimeMilliSec DomainLookupEnd(); |
324 | | DOMTimeMilliSec ConnectStart(); |
325 | | DOMTimeMilliSec SecureConnectionStart(); |
326 | | DOMTimeMilliSec ConnectEnd(); |
327 | | DOMTimeMilliSec RequestStart(); |
328 | | DOMTimeMilliSec ResponseStart(); |
329 | | DOMTimeMilliSec ResponseEnd(); |
330 | | |
331 | | DOMTimeMilliSec DomLoading() |
332 | | { |
333 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
334 | | nsContentUtils::ShouldResistFingerprinting()) { |
335 | | return 0; |
336 | | } |
337 | | if (mPerformance->IsSystemPrincipal()) { |
338 | | return GetDOMTiming()->GetDomLoading(); |
339 | | } |
340 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
341 | | GetDOMTiming()->GetDomLoading(), |
342 | | mPerformance->GetRandomTimelineSeed()); |
343 | | } |
344 | | |
345 | | DOMTimeMilliSec DomInteractive() const |
346 | | { |
347 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
348 | | nsContentUtils::ShouldResistFingerprinting()) { |
349 | | return 0; |
350 | | } |
351 | | if (mPerformance->IsSystemPrincipal()) { |
352 | | return GetDOMTiming()->GetDomInteractive(); |
353 | | } |
354 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
355 | | GetDOMTiming()->GetDomInteractive(), |
356 | | mPerformance->GetRandomTimelineSeed()); |
357 | | } |
358 | | |
359 | | DOMTimeMilliSec DomContentLoadedEventStart() const |
360 | | { |
361 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
362 | | nsContentUtils::ShouldResistFingerprinting()) { |
363 | | return 0; |
364 | | } |
365 | | if (mPerformance->IsSystemPrincipal()) { |
366 | | return GetDOMTiming()->GetDomContentLoadedEventStart(); |
367 | | } |
368 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
369 | | GetDOMTiming()->GetDomContentLoadedEventStart(), |
370 | | mPerformance->GetRandomTimelineSeed()); |
371 | | } |
372 | | |
373 | | DOMTimeMilliSec DomContentLoadedEventEnd() const |
374 | | { |
375 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
376 | | nsContentUtils::ShouldResistFingerprinting()) { |
377 | | return 0; |
378 | | } |
379 | | if (mPerformance->IsSystemPrincipal()) { |
380 | | return GetDOMTiming()->GetDomContentLoadedEventEnd(); |
381 | | } |
382 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
383 | | GetDOMTiming()->GetDomContentLoadedEventEnd(), |
384 | | mPerformance->GetRandomTimelineSeed()); |
385 | | } |
386 | | |
387 | | DOMTimeMilliSec DomComplete() const |
388 | | { |
389 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
390 | | nsContentUtils::ShouldResistFingerprinting()) { |
391 | | return 0; |
392 | | } |
393 | | if (mPerformance->IsSystemPrincipal()) { |
394 | | return GetDOMTiming()->GetDomComplete(); |
395 | | } |
396 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
397 | | GetDOMTiming()->GetDomComplete(), |
398 | | mPerformance->GetRandomTimelineSeed()); |
399 | | } |
400 | | |
401 | | DOMTimeMilliSec LoadEventStart() const |
402 | | { |
403 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
404 | | nsContentUtils::ShouldResistFingerprinting()) { |
405 | | return 0; |
406 | | } |
407 | | if (mPerformance->IsSystemPrincipal()) { |
408 | | return GetDOMTiming()->GetLoadEventStart(); |
409 | | } |
410 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
411 | | GetDOMTiming()->GetLoadEventStart(), |
412 | | mPerformance->GetRandomTimelineSeed()); |
413 | | } |
414 | | |
415 | | DOMTimeMilliSec LoadEventEnd() const |
416 | | { |
417 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
418 | | nsContentUtils::ShouldResistFingerprinting()) { |
419 | | return 0; |
420 | | } |
421 | | if (mPerformance->IsSystemPrincipal()) { |
422 | | return GetDOMTiming()->GetLoadEventEnd(); |
423 | | } |
424 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
425 | | GetDOMTiming()->GetLoadEventEnd(), |
426 | | mPerformance->GetRandomTimelineSeed()); |
427 | | } |
428 | | |
429 | | DOMTimeMilliSec TimeToNonBlankPaint() const |
430 | | { |
431 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
432 | | nsContentUtils::ShouldResistFingerprinting()) { |
433 | | return 0; |
434 | | } |
435 | | if (mPerformance->IsSystemPrincipal()) { |
436 | | return GetDOMTiming()->GetTimeToNonBlankPaint(); |
437 | | } |
438 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
439 | | GetDOMTiming()->GetTimeToNonBlankPaint(), |
440 | | mPerformance->GetRandomTimelineSeed()); |
441 | | } |
442 | | |
443 | | DOMTimeMilliSec TimeToDOMContentFlushed() const |
444 | | { |
445 | | if (!nsContentUtils::IsPerformanceTimingEnabled() || |
446 | | nsContentUtils::ShouldResistFingerprinting()) { |
447 | | return 0; |
448 | | } |
449 | | if (mPerformance->IsSystemPrincipal()) { |
450 | | return GetDOMTiming()->GetTimeToDOMContentFlushed(); |
451 | | } |
452 | | return nsRFPService::ReduceTimePrecisionAsMSecs( |
453 | | GetDOMTiming()->GetTimeToDOMContentFlushed(), |
454 | | mPerformance->GetRandomTimelineSeed()); |
455 | | } |
456 | | |
457 | | PerformanceTimingData* Data() const |
458 | 0 | { |
459 | 0 | return mTimingData.get(); |
460 | 0 | } |
461 | | |
462 | | private: |
463 | | ~PerformanceTiming(); |
464 | | |
465 | | bool IsTopLevelContentDocument() const; |
466 | | |
467 | | RefPtr<Performance> mPerformance; |
468 | | |
469 | | UniquePtr<PerformanceTimingData> mTimingData; |
470 | | }; |
471 | | |
472 | | } // namespace dom |
473 | | } // namespace mozilla |
474 | | |
475 | | #endif // mozilla_dom_PerformanceTiming_h |