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/. */
7 #include "Performance.h"
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/BasePrincipal.h"
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/dom/PerformanceBinding.h"
22 #include "mozilla/dom/PerformanceEntryEvent.h"
23 #include "mozilla/dom/PerformanceNavigationBinding.h"
24 #include "mozilla/dom/PerformanceObserverBinding.h"
25 #include "mozilla/dom/PerformanceNavigationTiming.h"
26 #include "mozilla/IntegerPrintfMacros.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/dom/WorkerPrivate.h"
29 #include "mozilla/dom/WorkerRunnable.h"
31 #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
33 namespace mozilla::dom
{
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance
)
36 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
38 NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance
, DOMEventTargetHelper
,
39 mUserEntries
, mResourceEntries
,
40 mSecondaryResourceEntries
, mObservers
);
42 NS_IMPL_ADDREF_INHERITED(Performance
, DOMEventTargetHelper
)
43 NS_IMPL_RELEASE_INHERITED(Performance
, DOMEventTargetHelper
)
46 already_AddRefed
<Performance
> Performance::CreateForMainThread(
47 nsPIDOMWindowInner
* aWindow
, nsIPrincipal
* aPrincipal
,
48 nsDOMNavigationTiming
* aDOMTiming
, nsITimedChannel
* aChannel
) {
49 MOZ_ASSERT(NS_IsMainThread());
51 MOZ_ASSERT(aWindow
->AsGlobal());
52 RefPtr
<Performance
> performance
= new PerformanceMainThread(
53 aWindow
, aDOMTiming
, aChannel
, aPrincipal
->IsSystemPrincipal());
54 return performance
.forget();
58 already_AddRefed
<Performance
> Performance::CreateForWorker(
59 WorkerPrivate
* aWorkerPrivate
) {
60 MOZ_ASSERT(aWorkerPrivate
);
61 aWorkerPrivate
->AssertIsOnWorkerThread();
63 RefPtr
<Performance
> performance
= new PerformanceWorker(aWorkerPrivate
);
64 return performance
.forget();
67 Performance::Performance(nsIGlobalObject
* aGlobal
, bool aSystemPrincipal
)
68 : DOMEventTargetHelper(aGlobal
),
69 mResourceTimingBufferSize(kDefaultResourceTimingBufferSize
),
70 mPendingNotificationObserversTask(false),
71 mPendingResourceTimingBufferFullEvent(false),
72 mSystemPrincipal(aSystemPrincipal
) {
73 MOZ_ASSERT(!NS_IsMainThread());
76 Performance::Performance(nsPIDOMWindowInner
* aWindow
, bool aSystemPrincipal
)
77 : DOMEventTargetHelper(aWindow
),
78 mResourceTimingBufferSize(kDefaultResourceTimingBufferSize
),
79 mPendingNotificationObserversTask(false),
80 mPendingResourceTimingBufferFullEvent(false),
81 mSystemPrincipal(aSystemPrincipal
) {
82 MOZ_ASSERT(NS_IsMainThread());
85 Performance::~Performance() = default;
87 DOMHighResTimeStamp
Performance::TimeStampToDOMHighResForRendering(
88 TimeStamp aTimeStamp
) const {
89 DOMHighResTimeStamp stamp
= GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp
);
90 if (!IsSystemPrincipal()) {
91 // 0 is an inappropriate mixin for this this area; however CSS Animations
92 // needs to have it's Time Reduction Logic refactored, so it's currently
93 // only clamping for RFP mode. RFP mode gives a much lower time precision,
94 // so we accept the security leak here for now.
95 return nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly(stamp
, 0);
100 DOMHighResTimeStamp
Performance::Now() {
101 DOMHighResTimeStamp rawTime
= NowUnclamped();
103 // XXX: Remove this would cause functions in pkcs11f.h to fail.
104 // Bug 1628021 will find out the root cause.
105 if (mSystemPrincipal
) {
109 return nsRFPService::ReduceTimePrecisionAsMSecs(
110 rawTime
, GetRandomTimelineSeed(), mSystemPrincipal
,
111 CrossOriginIsolated());
114 DOMHighResTimeStamp
Performance::NowUnclamped() const {
115 TimeDuration duration
= TimeStamp::Now() - CreationTimeStamp();
116 return duration
.ToMilliseconds();
119 DOMHighResTimeStamp
Performance::TimeOrigin() {
120 if (!mPerformanceService
) {
121 mPerformanceService
= PerformanceService::GetOrCreate();
124 MOZ_ASSERT(mPerformanceService
);
125 DOMHighResTimeStamp rawTimeOrigin
=
126 mPerformanceService
->TimeOrigin(CreationTimeStamp());
127 // Time Origin is an absolute timestamp, so we supply a 0 context mix-in
128 return nsRFPService::ReduceTimePrecisionAsMSecs(
129 rawTimeOrigin
, 0, mSystemPrincipal
, CrossOriginIsolated());
132 JSObject
* Performance::WrapObject(JSContext
* aCx
,
133 JS::Handle
<JSObject
*> aGivenProto
) {
134 return Performance_Binding::Wrap(aCx
, this, aGivenProto
);
137 void Performance::GetEntries(nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
138 // We return an empty list when 'privacy.resistFingerprinting' is on.
139 if (nsContentUtils::ShouldResistFingerprinting()) {
144 aRetval
= mResourceEntries
.Clone();
145 aRetval
.AppendElements(mUserEntries
);
146 aRetval
.Sort(PerformanceEntryComparator());
149 void Performance::GetEntriesByType(
150 const nsAString
& aEntryType
, nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
151 // We return an empty list when 'privacy.resistFingerprinting' is on.
152 if (nsContentUtils::ShouldResistFingerprinting()) {
157 if (aEntryType
.EqualsLiteral("resource")) {
158 aRetval
= mResourceEntries
.Clone();
164 if (aEntryType
.EqualsLiteral("mark") || aEntryType
.EqualsLiteral("measure")) {
165 RefPtr
<nsAtom
> entryType
= NS_Atomize(aEntryType
);
166 for (PerformanceEntry
* entry
: mUserEntries
) {
167 if (entry
->GetEntryType() == entryType
) {
168 aRetval
.AppendElement(entry
);
174 void Performance::GetEntriesByName(
175 const nsAString
& aName
, const Optional
<nsAString
>& aEntryType
,
176 nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
179 // We return an empty list when 'privacy.resistFingerprinting' is on.
180 if (nsContentUtils::ShouldResistFingerprinting()) {
184 RefPtr
<nsAtom
> name
= NS_Atomize(aName
);
185 RefPtr
<nsAtom
> entryType
=
186 aEntryType
.WasPassed() ? NS_Atomize(aEntryType
.Value()) : nullptr;
189 if (entryType
== nsGkAtoms::mark
|| entryType
== nsGkAtoms::measure
) {
190 for (PerformanceEntry
* entry
: mUserEntries
) {
191 if (entry
->GetName() == name
&& entry
->GetEntryType() == entryType
) {
192 aRetval
.AppendElement(entry
);
197 if (entryType
== nsGkAtoms::resource
) {
198 for (PerformanceEntry
* entry
: mResourceEntries
) {
199 MOZ_ASSERT(entry
->GetEntryType() == entryType
);
200 if (entry
->GetName() == name
) {
201 aRetval
.AppendElement(entry
);
210 nsTArray
<PerformanceEntry
*> qualifiedResourceEntries
;
211 nsTArray
<PerformanceEntry
*> qualifiedUserEntries
;
212 // ::Measure expects that results from this function are already
213 // passed through ReduceTimePrecision. mResourceEntries and mUserEntries
214 // are, so the invariant holds.
215 for (PerformanceEntry
* entry
: mResourceEntries
) {
216 if (entry
->GetName() == name
) {
217 qualifiedResourceEntries
.AppendElement(entry
);
221 for (PerformanceEntry
* entry
: mUserEntries
) {
222 if (entry
->GetName() == name
) {
223 qualifiedUserEntries
.AppendElement(entry
);
227 size_t resourceEntriesIdx
= 0, userEntriesIdx
= 0;
228 aRetval
.SetCapacity(qualifiedResourceEntries
.Length() +
229 qualifiedUserEntries
.Length());
231 PerformanceEntryComparator comparator
;
233 while (resourceEntriesIdx
< qualifiedResourceEntries
.Length() &&
234 userEntriesIdx
< qualifiedUserEntries
.Length()) {
235 if (comparator
.LessThan(qualifiedResourceEntries
[resourceEntriesIdx
],
236 qualifiedUserEntries
[userEntriesIdx
])) {
237 aRetval
.AppendElement(qualifiedResourceEntries
[resourceEntriesIdx
]);
238 ++resourceEntriesIdx
;
240 aRetval
.AppendElement(qualifiedUserEntries
[userEntriesIdx
]);
245 while (resourceEntriesIdx
< qualifiedResourceEntries
.Length()) {
246 aRetval
.AppendElement(qualifiedResourceEntries
[resourceEntriesIdx
]);
247 ++resourceEntriesIdx
;
250 while (userEntriesIdx
< qualifiedUserEntries
.Length()) {
251 aRetval
.AppendElement(qualifiedUserEntries
[userEntriesIdx
]);
256 void Performance::GetEntriesByTypeForObserver(
257 const nsAString
& aEntryType
, nsTArray
<RefPtr
<PerformanceEntry
>>& aRetval
) {
258 GetEntriesByType(aEntryType
, aRetval
);
261 void Performance::ClearUserEntries(const Optional
<nsAString
>& aEntryName
,
262 const nsAString
& aEntryType
) {
263 MOZ_ASSERT(!aEntryType
.IsEmpty());
264 RefPtr
<nsAtom
> name
=
265 aEntryName
.WasPassed() ? NS_Atomize(aEntryName
.Value()) : nullptr;
266 RefPtr
<nsAtom
> entryType
= NS_Atomize(aEntryType
);
267 mUserEntries
.RemoveElementsBy([name
, entryType
](const auto& entry
) {
268 return (!name
|| entry
->GetName() == name
) &&
269 (entry
->GetEntryType() == entryType
);
273 void Performance::ClearResourceTimings() { mResourceEntries
.Clear(); }
275 struct UserTimingMarker
{
276 static constexpr Span
<const char> MarkerTypeName() {
277 return MakeStringSpan("UserTiming");
279 static void StreamJSONMarkerData(
280 baseprofiler::SpliceableJSONWriter
& aWriter
,
281 const ProfilerString16View
& aName
, bool aIsMeasure
,
282 const Maybe
<ProfilerString16View
>& aStartMark
,
283 const Maybe
<ProfilerString16View
>& aEndMark
) {
284 aWriter
.StringProperty("name", NS_ConvertUTF16toUTF8(aName
));
286 aWriter
.StringProperty("entryType", "measure");
288 aWriter
.StringProperty("entryType", "mark");
291 if (aStartMark
.isSome()) {
292 aWriter
.StringProperty("startMark", NS_ConvertUTF16toUTF8(*aStartMark
));
294 aWriter
.NullProperty("startMark");
296 if (aEndMark
.isSome()) {
297 aWriter
.StringProperty("endMark", NS_ConvertUTF16toUTF8(*aEndMark
));
299 aWriter
.NullProperty("endMark");
302 static MarkerSchema
MarkerTypeDisplay() {
303 using MS
= MarkerSchema
;
304 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
305 schema
.SetAllLabels("{marker.data.name}");
306 schema
.AddStaticLabelValue("Marker", "UserTiming");
307 schema
.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::String
);
308 schema
.AddKeyLabelFormat("name", "Name", MS::Format::String
);
309 schema
.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::String
);
310 schema
.AddKeyLabelFormat("endMark", "End Mark", MS::Format::String
);
311 schema
.AddStaticLabelValue("Description",
312 "UserTimingMeasure is created using the DOM API "
313 "performance.measure().");
318 void Performance::Mark(const nsAString
& aName
, ErrorResult
& aRv
) {
319 // We add nothing when 'privacy.resistFingerprinting' is on.
320 if (nsContentUtils::ShouldResistFingerprinting()) {
324 if (IsPerformanceTimingAttribute(aName
)) {
325 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
329 RefPtr
<PerformanceMark
> performanceMark
=
330 new PerformanceMark(GetParentObject(), aName
, Now());
331 InsertUserEntry(performanceMark
);
333 if (profiler_thread_is_being_profiled_for_markers()) {
334 Maybe
<uint64_t> innerWindowId
;
336 innerWindowId
= Some(GetOwner()->WindowID());
338 profiler_add_marker("UserTiming", geckoprofiler::category::DOM
,
339 MarkerInnerWindowId(innerWindowId
), UserTimingMarker
{},
340 aName
, /* aIsMeasure */ false, Nothing
{}, Nothing
{});
344 void Performance::ClearMarks(const Optional
<nsAString
>& aName
) {
345 ClearUserEntries(aName
, u
"mark"_ns
);
348 DOMHighResTimeStamp
Performance::ResolveTimestampFromName(
349 const nsAString
& aName
, ErrorResult
& aRv
) {
350 AutoTArray
<RefPtr
<PerformanceEntry
>, 1> arr
;
351 Optional
<nsAString
> typeParam
;
353 str
.AssignLiteral("mark");
355 GetEntriesByName(aName
, typeParam
, arr
);
356 if (!arr
.IsEmpty()) {
357 return arr
.LastElement()->StartTime();
360 if (!IsPerformanceTimingAttribute(aName
)) {
361 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
365 DOMHighResTimeStamp ts
= GetPerformanceTimingFromString(aName
);
367 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
371 return ts
- CreationTime();
374 void Performance::Measure(const nsAString
& aName
,
375 const Optional
<nsAString
>& aStartMark
,
376 const Optional
<nsAString
>& aEndMark
,
378 // We add nothing when 'privacy.resistFingerprinting' is on.
379 if (nsContentUtils::ShouldResistFingerprinting()) {
383 DOMHighResTimeStamp startTime
;
384 DOMHighResTimeStamp endTime
;
386 if (aStartMark
.WasPassed()) {
387 startTime
= ResolveTimestampFromName(aStartMark
.Value(), aRv
);
388 if (NS_WARN_IF(aRv
.Failed())) {
392 // Navigation start is used in this case, but since DOMHighResTimeStamp is
393 // in relation to navigation start, this will be zero if a name is not
398 if (aEndMark
.WasPassed()) {
399 endTime
= ResolveTimestampFromName(aEndMark
.Value(), aRv
);
400 if (NS_WARN_IF(aRv
.Failed())) {
407 RefPtr
<PerformanceMeasure
> performanceMeasure
=
408 new PerformanceMeasure(GetParentObject(), aName
, startTime
, endTime
);
409 InsertUserEntry(performanceMeasure
);
411 if (profiler_thread_is_being_profiled_for_markers()) {
412 TimeStamp startTimeStamp
=
413 CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime
);
414 TimeStamp endTimeStamp
=
415 CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime
);
417 // Convert to Maybe values so that Optional types do not need to be used in
419 Maybe
<nsString
> startMark
;
420 if (aStartMark
.WasPassed()) {
421 startMark
.emplace(aStartMark
.Value());
423 Maybe
<nsString
> endMark
;
424 if (aEndMark
.WasPassed()) {
425 endMark
.emplace(aEndMark
.Value());
428 Maybe
<uint64_t> innerWindowId
;
430 innerWindowId
= Some(GetOwner()->WindowID());
432 profiler_add_marker("UserTiming", geckoprofiler::category::DOM
,
433 {MarkerTiming::Interval(startTimeStamp
, endTimeStamp
),
434 MarkerInnerWindowId(innerWindowId
)},
435 UserTimingMarker
{}, aName
, /* aIsMeasure */ true,
440 void Performance::ClearMeasures(const Optional
<nsAString
>& aName
) {
441 ClearUserEntries(aName
, u
"measure"_ns
);
444 void Performance::LogEntry(PerformanceEntry
* aEntry
,
445 const nsACString
& aOwner
) const {
446 PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64
"\n",
447 aOwner
.BeginReading(),
448 NS_ConvertUTF16toUTF8(aEntry
->GetEntryType()->GetUTF16String()).get(),
449 NS_ConvertUTF16toUTF8(aEntry
->GetName()->GetUTF16String()).get(),
450 aEntry
->StartTime(), aEntry
->Duration(),
451 static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC
));
454 void Performance::TimingNotification(PerformanceEntry
* aEntry
,
455 const nsACString
& aOwner
,
457 PerformanceEntryEventInit init
;
458 init
.mBubbles
= false;
459 init
.mCancelable
= false;
460 aEntry
->GetName(init
.mName
);
461 aEntry
->GetEntryType(init
.mEntryType
);
462 init
.mStartTime
= aEntry
->StartTime();
463 init
.mDuration
= aEntry
->Duration();
464 init
.mEpoch
= aEpoch
;
465 CopyUTF8toUTF16(aOwner
, init
.mOrigin
);
467 RefPtr
<PerformanceEntryEvent
> perfEntryEvent
=
468 PerformanceEntryEvent::Constructor(this, u
"performanceentry"_ns
, init
);
470 nsCOMPtr
<EventTarget
> et
= do_QueryInterface(GetOwner());
472 et
->DispatchEvent(*perfEntryEvent
);
476 void Performance::InsertUserEntry(PerformanceEntry
* aEntry
) {
477 mUserEntries
.InsertElementSorted(aEntry
, PerformanceEntryComparator());
483 * Steps are labeled according to the description found at
484 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
488 void Performance::BufferEvent() {
490 * While resource timing secondary buffer is not empty,
491 * run the following substeps:
493 while (!mSecondaryResourceEntries
.IsEmpty()) {
494 uint32_t secondaryResourceEntriesBeforeCount
= 0;
495 uint32_t secondaryResourceEntriesAfterCount
= 0;
498 * Let number of excess entries before be resource
499 * timing secondary buffer current size.
501 secondaryResourceEntriesBeforeCount
= mSecondaryResourceEntries
.Length();
504 * If can add resource timing entry returns false,
505 * then fire an event named resourcetimingbufferfull
506 * at the Performance object.
508 if (!CanAddResourceTimingEntry()) {
509 DispatchBufferFullEvent();
513 * Run copy secondary buffer.
515 * While resource timing secondary buffer is not
516 * empty and can add resource timing entry returns
519 while (!mSecondaryResourceEntries
.IsEmpty() &&
520 CanAddResourceTimingEntry()) {
522 * Let entry be the oldest PerformanceResourceTiming
523 * in resource timing secondary buffer. Add entry to
524 * the end of performance entry buffer. Increment
525 * resource timing buffer current size by 1.
527 mResourceEntries
.InsertElementSorted(
528 mSecondaryResourceEntries
.ElementAt(0), PerformanceEntryComparator());
530 * Remove entry from resource timing secondary buffer.
531 * Decrement resource timing secondary buffer current
534 mSecondaryResourceEntries
.RemoveElementAt(0);
538 * Let number of excess entries after be resource
539 * timing secondary buffer current size.
541 secondaryResourceEntriesAfterCount
= mSecondaryResourceEntries
.Length();
544 * If number of excess entries before is lower than
545 * or equals number of excess entries after, then
546 * remove all entries from resource timing secondary
547 * buffer, set resource timing secondary buffer current
548 * size to 0, and abort these steps.
550 if (secondaryResourceEntriesBeforeCount
<=
551 secondaryResourceEntriesAfterCount
) {
552 mSecondaryResourceEntries
.Clear();
557 * Set resource timing buffer full event pending flag
560 mPendingResourceTimingBufferFullEvent
= false;
563 void Performance::SetResourceTimingBufferSize(uint64_t aMaxSize
) {
564 mResourceTimingBufferSize
= aMaxSize
;
568 * Steps are labeled according to the description found at
569 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
571 * Can Add Resource Timing Entry
573 MOZ_ALWAYS_INLINE
bool Performance::CanAddResourceTimingEntry() {
575 * If resource timing buffer current size is smaller than resource timing
576 * buffer size limit, return true. [Otherwise,] [r]eturn false.
578 return mResourceEntries
.Length() < mResourceTimingBufferSize
;
582 * Steps are labeled according to the description found at
583 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
585 * Add a PerformanceResourceTiming Entry
587 void Performance::InsertResourceEntry(PerformanceEntry
* aEntry
) {
590 if (nsContentUtils::ShouldResistFingerprinting()) {
595 * Let new entry be the input PerformanceEntry to be added.
597 * If can add resource timing entry returns true and resource
598 * timing buffer full event pending flag is false ...
600 if (CanAddResourceTimingEntry() && !mPendingResourceTimingBufferFullEvent
) {
602 * Add new entry to the performance entry buffer.
603 * Increase resource timing buffer current size by 1.
605 mResourceEntries
.InsertElementSorted(aEntry
, PerformanceEntryComparator());
611 * If resource timing buffer full event pending flag is
614 if (!mPendingResourceTimingBufferFullEvent
) {
616 * Set resource timing buffer full event pending flag
619 mPendingResourceTimingBufferFullEvent
= true;
622 * Queue a task to run fire a buffer full event.
624 NS_DispatchToCurrentThread(NewCancelableRunnableMethod(
625 "Performance::BufferEvent", this, &Performance::BufferEvent
));
628 * Add new entry to the resource timing secondary buffer.
629 * Increase resource timing secondary buffer current size
632 mSecondaryResourceEntries
.InsertElementSorted(aEntry
,
633 PerformanceEntryComparator());
636 void Performance::AddObserver(PerformanceObserver
* aObserver
) {
637 mObservers
.AppendElementUnlessExists(aObserver
);
640 void Performance::RemoveObserver(PerformanceObserver
* aObserver
) {
641 mObservers
.RemoveElement(aObserver
);
644 void Performance::NotifyObservers() {
645 mPendingNotificationObserversTask
= false;
646 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers
, Notify
, ());
649 void Performance::CancelNotificationObservers() {
650 mPendingNotificationObserversTask
= false;
653 class NotifyObserversTask final
: public CancelableRunnable
{
655 explicit NotifyObserversTask(Performance
* aPerformance
)
656 : CancelableRunnable("dom::NotifyObserversTask"),
657 mPerformance(aPerformance
) {
658 MOZ_ASSERT(mPerformance
);
661 // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now until Runnable::Run is
662 // MOZ_CAN_RUN_SCRIPT.
663 MOZ_CAN_RUN_SCRIPT_BOUNDARY
664 NS_IMETHOD
Run() override
{
665 MOZ_ASSERT(mPerformance
);
666 RefPtr
<Performance
> performance(mPerformance
);
667 performance
->NotifyObservers();
671 nsresult
Cancel() override
{
672 mPerformance
->CancelNotificationObservers();
673 mPerformance
= nullptr;
678 ~NotifyObserversTask() = default;
680 RefPtr
<Performance
> mPerformance
;
683 void Performance::QueueNotificationObserversTask() {
684 if (!mPendingNotificationObserversTask
) {
685 RunNotificationObserversTask();
689 void Performance::RunNotificationObserversTask() {
690 mPendingNotificationObserversTask
= true;
691 nsCOMPtr
<nsIRunnable
> task
= new NotifyObserversTask(this);
693 if (GetOwnerGlobal()) {
694 rv
= GetOwnerGlobal()->Dispatch(TaskCategory::Other
, task
.forget());
696 rv
= NS_DispatchToCurrentThread(task
);
698 if (NS_WARN_IF(NS_FAILED(rv
))) {
699 mPendingNotificationObserversTask
= false;
703 void Performance::QueueEntry(PerformanceEntry
* aEntry
) {
704 nsTObserverArray
<PerformanceObserver
*> interestedObservers
;
705 if (!mObservers
.IsEmpty()) {
706 const auto [begin
, end
] = mObservers
.NonObservingRange();
707 std::copy_if(begin
, end
, MakeBackInserter(interestedObservers
),
708 [aEntry
](PerformanceObserver
* observer
) {
709 return observer
->ObservesTypeOfEntry(aEntry
);
713 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(interestedObservers
, QueueEntry
,
716 aEntry
->BufferEntryIfNeeded();
718 if (!interestedObservers
.IsEmpty()) {
719 QueueNotificationObserversTask();
723 void Performance::MemoryPressure() { mUserEntries
.Clear(); }
725 size_t Performance::SizeOfUserEntries(
726 mozilla::MallocSizeOf aMallocSizeOf
) const {
727 size_t userEntries
= 0;
728 for (const PerformanceEntry
* entry
: mUserEntries
) {
729 userEntries
+= entry
->SizeOfIncludingThis(aMallocSizeOf
);
734 size_t Performance::SizeOfResourceEntries(
735 mozilla::MallocSizeOf aMallocSizeOf
) const {
736 size_t resourceEntries
= 0;
737 for (const PerformanceEntry
* entry
: mResourceEntries
) {
738 resourceEntries
+= entry
->SizeOfIncludingThis(aMallocSizeOf
);
740 return resourceEntries
;
743 } // namespace mozilla::dom