Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/performance/Performance.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 "Performance.h"
8
9
#include "GeckoProfiler.h"
10
#include "nsRFPService.h"
11
#include "PerformanceEntry.h"
12
#include "PerformanceMainThread.h"
13
#include "PerformanceMark.h"
14
#include "PerformanceMeasure.h"
15
#include "PerformanceObserver.h"
16
#include "PerformanceResourceTiming.h"
17
#include "PerformanceService.h"
18
#include "PerformanceWorker.h"
19
#include "mozilla/ErrorResult.h"
20
#include "mozilla/dom/PerformanceBinding.h"
21
#include "mozilla/dom/PerformanceEntryEvent.h"
22
#include "mozilla/dom/PerformanceNavigationBinding.h"
23
#include "mozilla/dom/PerformanceObserverBinding.h"
24
#include "mozilla/dom/PerformanceNavigationTiming.h"
25
#include "mozilla/IntegerPrintfMacros.h"
26
#include "mozilla/Preferences.h"
27
#include "mozilla/dom/WorkerPrivate.h"
28
#include "mozilla/dom/WorkerRunnable.h"
29
30
#ifdef MOZ_GECKO_PROFILER
31
#include "ProfilerMarkerPayload.h"
32
#endif
33
34
0
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
35
36
namespace mozilla {
37
namespace dom {
38
39
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance)
40
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
41
42
NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance,
43
                                   DOMEventTargetHelper,
44
                                   mUserEntries,
45
                                   mResourceEntries);
46
47
NS_IMPL_ADDREF_INHERITED(Performance, DOMEventTargetHelper)
48
NS_IMPL_RELEASE_INHERITED(Performance, DOMEventTargetHelper)
49
50
/* static */ already_AddRefed<Performance>
51
Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow,
52
                                 nsIPrincipal* aPrincipal,
53
                                 nsDOMNavigationTiming* aDOMTiming,
54
                                 nsITimedChannel* aChannel)
55
0
{
56
0
  MOZ_ASSERT(NS_IsMainThread());
57
0
58
0
  RefPtr<Performance> performance =
59
0
    new PerformanceMainThread(aWindow,
60
0
                              aDOMTiming,
61
0
                              aChannel,
62
0
                              nsContentUtils::IsSystemPrincipal(aPrincipal));
63
0
  return performance.forget();
64
0
}
65
66
/* static */ already_AddRefed<Performance>
67
Performance::CreateForWorker(WorkerPrivate* aWorkerPrivate)
68
0
{
69
0
  MOZ_ASSERT(aWorkerPrivate);
70
0
  aWorkerPrivate->AssertIsOnWorkerThread();
71
0
72
0
  RefPtr<Performance> performance = new PerformanceWorker(aWorkerPrivate);
73
0
  return performance.forget();
74
0
}
75
76
Performance::Performance(bool aSystemPrincipal)
77
  : mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
78
  , mPendingNotificationObserversTask(false)
79
  , mSystemPrincipal(aSystemPrincipal)
80
0
{
81
0
  MOZ_ASSERT(!NS_IsMainThread());
82
0
}
83
84
Performance::Performance(nsPIDOMWindowInner* aWindow, bool aSystemPrincipal)
85
  : DOMEventTargetHelper(aWindow)
86
  , mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
87
  , mPendingNotificationObserversTask(false)
88
  , mSystemPrincipal(aSystemPrincipal)
89
0
{
90
0
  MOZ_ASSERT(NS_IsMainThread());
91
0
}
92
93
Performance::~Performance()
94
0
{}
95
96
DOMHighResTimeStamp
97
Performance::Now()
98
0
{
99
0
  DOMHighResTimeStamp rawTime = NowUnclamped();
100
0
  if (mSystemPrincipal) {
101
0
    return rawTime;
102
0
  }
103
0
104
0
  const double maxResolutionMs = 0.020;
105
0
  DOMHighResTimeStamp minimallyClamped = floor(rawTime / maxResolutionMs) * maxResolutionMs;
106
0
  return nsRFPService::ReduceTimePrecisionAsMSecs(minimallyClamped, GetRandomTimelineSeed());
107
0
}
108
109
DOMHighResTimeStamp
110
Performance::NowUnclamped() const
111
0
{
112
0
  TimeDuration duration = TimeStamp::Now() - CreationTimeStamp();
113
0
  return duration.ToMilliseconds();
114
0
}
115
116
DOMHighResTimeStamp
117
Performance::TimeOrigin()
118
0
{
119
0
  if (!mPerformanceService) {
120
0
    mPerformanceService = PerformanceService::GetOrCreate();
121
0
  }
122
0
123
0
  MOZ_ASSERT(mPerformanceService);
124
0
  DOMHighResTimeStamp rawTimeOrigin = mPerformanceService->TimeOrigin(CreationTimeStamp());
125
0
  if (mSystemPrincipal) {
126
0
    return rawTimeOrigin;
127
0
  }
128
0
129
0
  // Time Origin is an absolute timestamp, so we supply a 0 context mix-in
130
0
  return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimeOrigin, 0);
131
0
}
132
133
JSObject*
134
Performance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
135
0
{
136
0
  return Performance_Binding::Wrap(aCx, this, aGivenProto);
137
0
}
138
139
void
140
Performance::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
141
0
{
142
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
143
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
144
0
    aRetval.Clear();
145
0
    return;
146
0
  }
147
0
148
0
  aRetval = mResourceEntries;
149
0
  aRetval.AppendElements(mUserEntries);
150
0
  aRetval.Sort(PerformanceEntryComparator());
151
0
}
152
153
void
154
Performance::GetEntriesByType(const nsAString& aEntryType,
155
                              nsTArray<RefPtr<PerformanceEntry>>& aRetval)
156
0
{
157
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
158
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
159
0
    aRetval.Clear();
160
0
    return;
161
0
  }
162
0
163
0
  if (aEntryType.EqualsLiteral("resource")) {
164
0
    aRetval = mResourceEntries;
165
0
    return;
166
0
  }
167
0
168
0
  aRetval.Clear();
169
0
170
0
  if (aEntryType.EqualsLiteral("mark") ||
171
0
      aEntryType.EqualsLiteral("measure")) {
172
0
    for (PerformanceEntry* entry : mUserEntries) {
173
0
      if (entry->GetEntryType().Equals(aEntryType)) {
174
0
        aRetval.AppendElement(entry);
175
0
      }
176
0
    }
177
0
  }
178
0
}
179
180
void
181
Performance::GetEntriesByName(const nsAString& aName,
182
                              const Optional<nsAString>& aEntryType,
183
                              nsTArray<RefPtr<PerformanceEntry>>& aRetval)
184
0
{
185
0
  aRetval.Clear();
186
0
187
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
188
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
189
0
    return;
190
0
  }
191
0
192
0
  for (PerformanceEntry* entry : mResourceEntries) {
193
0
    if (entry->GetName().Equals(aName) &&
194
0
        (!aEntryType.WasPassed() ||
195
0
         entry->GetEntryType().Equals(aEntryType.Value()))) {
196
0
      aRetval.AppendElement(entry);
197
0
    }
198
0
  }
199
0
200
0
  for (PerformanceEntry* entry : mUserEntries) {
201
0
    if (entry->GetName().Equals(aName) &&
202
0
        (!aEntryType.WasPassed() ||
203
0
         entry->GetEntryType().Equals(aEntryType.Value()))) {
204
0
      aRetval.AppendElement(entry);
205
0
    }
206
0
  }
207
0
208
0
  aRetval.Sort(PerformanceEntryComparator());
209
0
}
210
211
void
212
Performance::ClearUserEntries(const Optional<nsAString>& aEntryName,
213
                              const nsAString& aEntryType)
214
0
{
215
0
  for (uint32_t i = 0; i < mUserEntries.Length();) {
216
0
    if ((!aEntryName.WasPassed() ||
217
0
         mUserEntries[i]->GetName().Equals(aEntryName.Value())) &&
218
0
        (aEntryType.IsEmpty() ||
219
0
         mUserEntries[i]->GetEntryType().Equals(aEntryType))) {
220
0
      mUserEntries.RemoveElementAt(i);
221
0
    } else {
222
0
      ++i;
223
0
    }
224
0
  }
225
0
}
226
227
void
228
Performance::ClearResourceTimings()
229
0
{
230
0
  mResourceEntries.Clear();
231
0
}
232
233
void
234
Performance::Mark(const nsAString& aName, ErrorResult& aRv)
235
0
{
236
0
  // We add nothing when 'privacy.resistFingerprinting' is on.
237
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
238
0
    return;
239
0
  }
240
0
241
0
  if (IsPerformanceTimingAttribute(aName)) {
242
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
243
0
    return;
244
0
  }
245
0
246
0
  RefPtr<PerformanceMark> performanceMark =
247
0
    new PerformanceMark(GetParentObject(), aName, Now());
248
0
  InsertUserEntry(performanceMark);
249
0
250
0
#ifdef MOZ_GECKO_PROFILER
251
0
  if (profiler_is_active()) {
252
0
    profiler_add_marker(
253
0
      "UserTiming",
254
0
      MakeUnique<UserTimingMarkerPayload>(aName, TimeStamp::Now()));
255
0
  }
256
0
#endif
257
0
}
258
259
void
260
Performance::ClearMarks(const Optional<nsAString>& aName)
261
0
{
262
0
  ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
263
0
}
264
265
DOMHighResTimeStamp
266
Performance::ResolveTimestampFromName(const nsAString& aName,
267
                                      ErrorResult& aRv)
268
0
{
269
0
  AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
270
0
  Optional<nsAString> typeParam;
271
0
  nsAutoString str;
272
0
  str.AssignLiteral("mark");
273
0
  typeParam = &str;
274
0
  GetEntriesByName(aName, typeParam, arr);
275
0
  if (!arr.IsEmpty()) {
276
0
    return arr.LastElement()->StartTime();
277
0
  }
278
0
279
0
  if (!IsPerformanceTimingAttribute(aName)) {
280
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
281
0
    return 0;
282
0
  }
283
0
284
0
  DOMHighResTimeStamp ts = GetPerformanceTimingFromString(aName);
285
0
  if (!ts) {
286
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
287
0
    return 0;
288
0
  }
289
0
290
0
  return ts - CreationTime();
291
0
}
292
293
void
294
Performance::Measure(const nsAString& aName,
295
                     const Optional<nsAString>& aStartMark,
296
                     const Optional<nsAString>& aEndMark,
297
                     ErrorResult& aRv)
298
0
{
299
0
  // We add nothing when 'privacy.resistFingerprinting' is on.
300
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
301
0
    return;
302
0
  }
303
0
304
0
  DOMHighResTimeStamp startTime;
305
0
  DOMHighResTimeStamp endTime;
306
0
307
0
  if (aStartMark.WasPassed()) {
308
0
    startTime = ResolveTimestampFromName(aStartMark.Value(), aRv);
309
0
    if (NS_WARN_IF(aRv.Failed())) {
310
0
      return;
311
0
    }
312
0
  } else {
313
0
    // Navigation start is used in this case, but since DOMHighResTimeStamp is
314
0
    // in relation to navigation start, this will be zero if a name is not
315
0
    // passed.
316
0
    startTime = 0;
317
0
  }
318
0
319
0
  if (aEndMark.WasPassed()) {
320
0
    endTime = ResolveTimestampFromName(aEndMark.Value(), aRv);
321
0
    if (NS_WARN_IF(aRv.Failed())) {
322
0
      return;
323
0
    }
324
0
  } else {
325
0
    endTime = Now();
326
0
  }
327
0
328
0
  RefPtr<PerformanceMeasure> performanceMeasure =
329
0
    new PerformanceMeasure(GetParentObject(), aName, startTime, endTime);
330
0
  InsertUserEntry(performanceMeasure);
331
0
332
0
#ifdef MOZ_GECKO_PROFILER
333
0
  if (profiler_is_active()) {
334
0
    TimeStamp startTimeStamp = CreationTimeStamp() +
335
0
                               TimeDuration::FromMilliseconds(startTime);
336
0
    TimeStamp endTimeStamp = CreationTimeStamp() +
337
0
                             TimeDuration::FromMilliseconds(endTime);
338
0
339
0
    // Convert to Maybe values so that Optional types do not need to be used in
340
0
    // the profiler.
341
0
    Maybe<nsString> startMark;
342
0
    if (aStartMark.WasPassed()) {
343
0
      startMark.emplace(aStartMark.Value());
344
0
    }
345
0
    Maybe<nsString> endMark;
346
0
    if (aEndMark.WasPassed()) {
347
0
      endMark.emplace(aEndMark.Value());
348
0
    }
349
0
350
0
    profiler_add_marker(
351
0
      "UserTiming",
352
0
      MakeUnique<UserTimingMarkerPayload>(aName, startMark, endMark,
353
0
                                          startTimeStamp, endTimeStamp));
354
0
  }
355
0
#endif
356
0
}
357
358
void
359
Performance::ClearMeasures(const Optional<nsAString>& aName)
360
0
{
361
0
  ClearUserEntries(aName, NS_LITERAL_STRING("measure"));
362
0
}
363
364
void
365
Performance::LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const
366
0
{
367
0
  PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
368
0
          aOwner.BeginReading(),
369
0
          NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
370
0
          NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
371
0
          aEntry->StartTime(),
372
0
          aEntry->Duration(),
373
0
          static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
374
0
}
375
376
void
377
Performance::TimingNotification(PerformanceEntry* aEntry,
378
                                const nsACString& aOwner, uint64_t aEpoch)
379
0
{
380
0
  PerformanceEntryEventInit init;
381
0
  init.mBubbles = false;
382
0
  init.mCancelable = false;
383
0
  init.mName = aEntry->GetName();
384
0
  init.mEntryType = aEntry->GetEntryType();
385
0
  init.mStartTime = aEntry->StartTime();
386
0
  init.mDuration = aEntry->Duration();
387
0
  init.mEpoch = aEpoch;
388
0
  init.mOrigin = NS_ConvertUTF8toUTF16(aOwner.BeginReading());
389
0
390
0
  RefPtr<PerformanceEntryEvent> perfEntryEvent =
391
0
    PerformanceEntryEvent::Constructor(this, NS_LITERAL_STRING("performanceentry"), init);
392
0
393
0
  nsCOMPtr<EventTarget> et = do_QueryInterface(GetOwner());
394
0
  if (et) {
395
0
    et->DispatchEvent(*perfEntryEvent);
396
0
  }
397
0
}
398
399
void
400
Performance::InsertUserEntry(PerformanceEntry* aEntry)
401
0
{
402
0
  mUserEntries.InsertElementSorted(aEntry,
403
0
                                   PerformanceEntryComparator());
404
0
405
0
  QueueEntry(aEntry);
406
0
}
407
408
void
409
Performance::SetResourceTimingBufferSize(uint64_t aMaxSize)
410
0
{
411
0
  mResourceTimingBufferSize = aMaxSize;
412
0
}
413
414
void
415
Performance::InsertResourceEntry(PerformanceEntry* aEntry)
416
0
{
417
0
  MOZ_ASSERT(aEntry);
418
0
419
0
  // We won't add an entry when 'privacy.resistFingerprint' is true.
420
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
421
0
    return;
422
0
  }
423
0
424
0
  // Don't add the entry if the buffer is full
425
0
  if (mResourceEntries.Length() >= mResourceTimingBufferSize) {
426
0
    return;
427
0
  }
428
0
429
0
  mResourceEntries.InsertElementSorted(aEntry,
430
0
                                       PerformanceEntryComparator());
431
0
  if (mResourceEntries.Length() == mResourceTimingBufferSize) {
432
0
    // call onresourcetimingbufferfull
433
0
    DispatchBufferFullEvent();
434
0
  }
435
0
  QueueEntry(aEntry);
436
0
}
437
438
void
439
Performance::AddObserver(PerformanceObserver* aObserver)
440
0
{
441
0
  mObservers.AppendElementUnlessExists(aObserver);
442
0
}
443
444
void
445
Performance::RemoveObserver(PerformanceObserver* aObserver)
446
0
{
447
0
  mObservers.RemoveElement(aObserver);
448
0
}
449
450
void
451
Performance::NotifyObservers()
452
0
{
453
0
  mPendingNotificationObserversTask = false;
454
0
  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
455
0
                                           PerformanceObserver,
456
0
                                           Notify, ());
457
0
}
458
459
void
460
Performance::CancelNotificationObservers()
461
0
{
462
0
  mPendingNotificationObserversTask = false;
463
0
}
464
465
class NotifyObserversTask final : public CancelableRunnable
466
{
467
public:
468
  explicit NotifyObserversTask(Performance* aPerformance)
469
    : CancelableRunnable("dom::NotifyObserversTask")
470
    , mPerformance(aPerformance)
471
0
  {
472
0
    MOZ_ASSERT(mPerformance);
473
0
  }
474
475
  NS_IMETHOD Run() override
476
0
  {
477
0
    MOZ_ASSERT(mPerformance);
478
0
    mPerformance->NotifyObservers();
479
0
    return NS_OK;
480
0
  }
481
482
  nsresult Cancel() override
483
0
  {
484
0
    mPerformance->CancelNotificationObservers();
485
0
    mPerformance = nullptr;
486
0
    return NS_OK;
487
0
  }
488
489
private:
490
  ~NotifyObserversTask()
491
0
  {
492
0
  }
493
494
  RefPtr<Performance> mPerformance;
495
};
496
497
void
498
Performance::RunNotificationObserversTask()
499
0
{
500
0
  mPendingNotificationObserversTask = true;
501
0
  nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this);
502
0
  nsresult rv;
503
0
  if (GetOwnerGlobal()) {
504
0
    rv = GetOwnerGlobal()->Dispatch(TaskCategory::Other, task.forget());
505
0
  } else {
506
0
    rv = NS_DispatchToCurrentThread(task);
507
0
  }
508
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
509
0
    mPendingNotificationObserversTask = false;
510
0
  }
511
0
}
512
513
void
514
Performance::QueueEntry(PerformanceEntry* aEntry)
515
0
{
516
0
  if (mObservers.IsEmpty()) {
517
0
    return;
518
0
  }
519
0
  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
520
0
                                           PerformanceObserver,
521
0
                                           QueueEntry, (aEntry));
522
0
523
0
  if (!mPendingNotificationObserversTask) {
524
0
    RunNotificationObserversTask();
525
0
  }
526
0
}
527
528
void
529
Performance::MemoryPressure()
530
0
{
531
0
  mUserEntries.Clear();
532
0
}
533
534
size_t
535
Performance::SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const
536
0
{
537
0
  size_t userEntries = 0;
538
0
  for (const PerformanceEntry* entry : mUserEntries) {
539
0
    userEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
540
0
  }
541
0
  return userEntries;
542
0
}
543
544
size_t
545
Performance::SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const
546
0
{
547
0
  size_t resourceEntries = 0;
548
0
  for (const PerformanceEntry* entry : mResourceEntries) {
549
0
    resourceEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
550
0
  }
551
0
  return resourceEntries;
552
0
}
553
554
} // dom namespace
555
} // mozilla namespace