Coverage Report

Created: 2018-09-25 14:53

/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