Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / performance / Performance.cpp
blob024e252a8e0367500d23857828839905ff6170a7
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)
45 /* static */
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();
57 /* static */
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);
97 return stamp;
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) {
106 return rawTime;
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()) {
140 aRetval.Clear();
141 return;
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()) {
153 aRetval.Clear();
154 return;
157 if (aEntryType.EqualsLiteral("resource")) {
158 aRetval = mResourceEntries.Clone();
159 return;
162 aRetval.Clear();
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) {
177 aRetval.Clear();
179 // We return an empty list when 'privacy.resistFingerprinting' is on.
180 if (nsContentUtils::ShouldResistFingerprinting()) {
181 return;
184 RefPtr<nsAtom> name = NS_Atomize(aName);
185 RefPtr<nsAtom> entryType =
186 aEntryType.WasPassed() ? NS_Atomize(aEntryType.Value()) : nullptr;
188 if (entryType) {
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);
195 return;
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);
204 return;
206 // Invalid entryType
207 return;
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;
239 } else {
240 aRetval.AppendElement(qualifiedUserEntries[userEntriesIdx]);
241 ++userEntriesIdx;
245 while (resourceEntriesIdx < qualifiedResourceEntries.Length()) {
246 aRetval.AppendElement(qualifiedResourceEntries[resourceEntriesIdx]);
247 ++resourceEntriesIdx;
250 while (userEntriesIdx < qualifiedUserEntries.Length()) {
251 aRetval.AppendElement(qualifiedUserEntries[userEntriesIdx]);
252 ++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));
285 if (aIsMeasure) {
286 aWriter.StringProperty("entryType", "measure");
287 } else {
288 aWriter.StringProperty("entryType", "mark");
291 if (aStartMark.isSome()) {
292 aWriter.StringProperty("startMark", NS_ConvertUTF16toUTF8(*aStartMark));
293 } else {
294 aWriter.NullProperty("startMark");
296 if (aEndMark.isSome()) {
297 aWriter.StringProperty("endMark", NS_ConvertUTF16toUTF8(*aEndMark));
298 } else {
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().");
314 return schema;
318 void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
319 // We add nothing when 'privacy.resistFingerprinting' is on.
320 if (nsContentUtils::ShouldResistFingerprinting()) {
321 return;
324 if (IsPerformanceTimingAttribute(aName)) {
325 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
326 return;
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;
335 if (GetOwner()) {
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;
352 nsAutoString str;
353 str.AssignLiteral("mark");
354 typeParam = &str;
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);
362 return 0;
365 DOMHighResTimeStamp ts = GetPerformanceTimingFromString(aName);
366 if (!ts) {
367 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
368 return 0;
371 return ts - CreationTime();
374 void Performance::Measure(const nsAString& aName,
375 const Optional<nsAString>& aStartMark,
376 const Optional<nsAString>& aEndMark,
377 ErrorResult& aRv) {
378 // We add nothing when 'privacy.resistFingerprinting' is on.
379 if (nsContentUtils::ShouldResistFingerprinting()) {
380 return;
383 DOMHighResTimeStamp startTime;
384 DOMHighResTimeStamp endTime;
386 if (aStartMark.WasPassed()) {
387 startTime = ResolveTimestampFromName(aStartMark.Value(), aRv);
388 if (NS_WARN_IF(aRv.Failed())) {
389 return;
391 } else {
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
394 // passed.
395 startTime = 0;
398 if (aEndMark.WasPassed()) {
399 endTime = ResolveTimestampFromName(aEndMark.Value(), aRv);
400 if (NS_WARN_IF(aRv.Failed())) {
401 return;
403 } else {
404 endTime = Now();
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
418 // the profiler.
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;
429 if (GetOwner()) {
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,
436 startMark, endMark);
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,
456 uint64_t aEpoch) {
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());
471 if (et) {
472 et->DispatchEvent(*perfEntryEvent);
476 void Performance::InsertUserEntry(PerformanceEntry* aEntry) {
477 mUserEntries.InsertElementSorted(aEntry, PerformanceEntryComparator());
479 QueueEntry(aEntry);
483 * Steps are labeled according to the description found at
484 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
486 * Buffer Full Event
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
517 * true ...
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
532 * size by 1.
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();
553 break;
557 * Set resource timing buffer full event pending flag
558 * to false.
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) {
588 MOZ_ASSERT(aEntry);
590 if (nsContentUtils::ShouldResistFingerprinting()) {
591 return;
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());
606 QueueEntry(aEntry);
607 return;
611 * If resource timing buffer full event pending flag is
612 * false ...
614 if (!mPendingResourceTimingBufferFullEvent) {
616 * Set resource timing buffer full event pending flag
617 * to true.
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
630 * by 1.
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 {
654 public:
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();
668 return NS_OK;
671 nsresult Cancel() override {
672 mPerformance->CancelNotificationObservers();
673 mPerformance = nullptr;
674 return NS_OK;
677 private:
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);
692 nsresult rv;
693 if (GetOwnerGlobal()) {
694 rv = GetOwnerGlobal()->Dispatch(TaskCategory::Other, task.forget());
695 } else {
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,
714 (aEntry));
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);
731 return userEntries;
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