Coverage Report

Created: 2018-09-25 14:53

/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