tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Performance.cpp (40801B)


      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 <sstream>
     10 
     11 #if defined(XP_LINUX)
     12 #  include <fcntl.h>
     13 #  include <sys/mman.h>
     14 #endif
     15 
     16 #include "ETWTools.h"
     17 #include "GeckoProfiler.h"
     18 #include "PerformanceEntry.h"
     19 #include "PerformanceMainThread.h"
     20 #include "PerformanceMark.h"
     21 #include "PerformanceMeasure.h"
     22 #include "PerformanceObserver.h"
     23 #include "PerformanceResourceTiming.h"
     24 #include "PerformanceService.h"
     25 #include "PerformanceWorker.h"
     26 #include "mozilla/BasePrincipal.h"
     27 #include "mozilla/ErrorResult.h"
     28 #include "mozilla/IntegerPrintfMacros.h"
     29 #include "mozilla/Perfetto.h"
     30 #include "mozilla/Preferences.h"
     31 #include "mozilla/TimeStamp.h"
     32 #include "mozilla/dom/MessagePortBinding.h"
     33 #include "mozilla/dom/PerformanceBinding.h"
     34 #include "mozilla/dom/PerformanceEntryEvent.h"
     35 #include "mozilla/dom/PerformanceNavigationBinding.h"
     36 #include "mozilla/dom/PerformanceNavigationTiming.h"
     37 #include "mozilla/dom/PerformanceObserverBinding.h"
     38 #include "mozilla/dom/WorkerPrivate.h"
     39 #include "mozilla/dom/WorkerRunnable.h"
     40 #include "mozilla/dom/WorkerScope.h"
     41 #include "nsGlobalWindowInner.h"
     42 #include "nsRFPService.h"
     43 
     44 #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
     45 
     46 namespace mozilla::dom {
     47 
     48 enum class Performance::ResolveTimestampAttribute {
     49  Start,
     50  End,
     51  Duration,
     52 };
     53 
     54 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance)
     55 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     56 
     57 NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance, DOMEventTargetHelper,
     58                                   mUserEntries, mResourceEntries,
     59                                   mSecondaryResourceEntries, mObservers);
     60 
     61 NS_IMPL_ADDREF_INHERITED(Performance, DOMEventTargetHelper)
     62 NS_IMPL_RELEASE_INHERITED(Performance, DOMEventTargetHelper)
     63 
     64 // Used to dump performance timing information to a local file.
     65 // Only defined when the env variable MOZ_USE_PERFORMANCE_MARKER_FILE
     66 // is set and initialized by MaybeOpenMarkerFile().
     67 static FILE* sMarkerFile = nullptr;
     68 
     69 /* static */
     70 already_AddRefed<Performance> Performance::CreateForMainThread(
     71    nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
     72    nsDOMNavigationTiming* aDOMTiming, nsITimedChannel* aChannel) {
     73  MOZ_ASSERT(NS_IsMainThread());
     74 
     75  MOZ_ASSERT(aWindow->AsGlobal());
     76  RefPtr<Performance> performance =
     77      new PerformanceMainThread(aWindow, aDOMTiming, aChannel);
     78  return performance.forget();
     79 }
     80 
     81 /* static */
     82 already_AddRefed<Performance> Performance::CreateForWorker(
     83    WorkerGlobalScope* aGlobalScope) {
     84  MOZ_ASSERT(aGlobalScope);
     85  //  aWorkerPrivate->AssertIsOnWorkerThread();
     86 
     87  RefPtr<Performance> performance = new PerformanceWorker(aGlobalScope);
     88  return performance.forget();
     89 }
     90 
     91 /* static */
     92 already_AddRefed<Performance> Performance::Get(JSContext* aCx,
     93                                               nsIGlobalObject* aGlobal) {
     94  RefPtr<Performance> performance;
     95  if (NS_IsMainThread()) {
     96    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
     97    if (!window) {
     98      return nullptr;
     99    }
    100 
    101    performance = window->GetPerformance();
    102    return performance.forget();
    103  }
    104 
    105  const WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
    106  if (!workerPrivate) {
    107    return nullptr;
    108  }
    109 
    110  WorkerGlobalScope* scope = workerPrivate->GlobalScope();
    111  MOZ_ASSERT(scope);
    112  performance = scope->GetPerformance();
    113 
    114  return performance.forget();
    115 }
    116 
    117 Performance::Performance(nsIGlobalObject* aGlobal)
    118    : DOMEventTargetHelper(aGlobal),
    119      mResourceTimingBufferSize(kDefaultResourceTimingBufferSize),
    120      mPendingNotificationObserversTask(false),
    121      mPendingResourceTimingBufferFullEvent(false),
    122      mRTPCallerType(aGlobal->GetRTPCallerType()),
    123      mCrossOriginIsolated(aGlobal->CrossOriginIsolated()),
    124      mShouldResistFingerprinting(aGlobal->ShouldResistFingerprinting(
    125          RFPTarget::ReduceTimerPrecision)) {}
    126 
    127 Performance::~Performance() = default;
    128 
    129 DOMHighResTimeStamp Performance::TimeStampToDOMHighResForRendering(
    130    TimeStamp aTimeStamp) const {
    131  DOMHighResTimeStamp stamp = GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
    132  // 0 is an inappropriate mixin for this this area; however CSS Animations
    133  // needs to have it's Time Reduction Logic refactored, so it's currently
    134  // only clamping for RFP mode. RFP mode gives a much lower time precision,
    135  // so we accept the security leak here for now.
    136  return nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly(stamp, 0,
    137                                                         mRTPCallerType);
    138 }
    139 
    140 DOMHighResTimeStamp Performance::Now() {
    141  DOMHighResTimeStamp rawTime = NowUnclamped();
    142 
    143  // XXX: Removing this caused functions in pkcs11f.h to fail.
    144  // Bug 1628021 investigates the root cause - it involves initializing
    145  // the RNG service (part of GetRandomTimelineSeed()) off-main-thread
    146  // but the underlying cause hasn't been identified yet.
    147  if (mRTPCallerType == RTPCallerType::SystemPrincipal) {
    148    return rawTime;
    149  }
    150 
    151  return nsRFPService::ReduceTimePrecisionAsMSecs(
    152      rawTime, GetRandomTimelineSeed(), mRTPCallerType);
    153 }
    154 
    155 DOMHighResTimeStamp Performance::NowUnclamped() const {
    156  TimeDuration duration = TimeStamp::Now() - CreationTimeStamp();
    157  return duration.ToMilliseconds();
    158 }
    159 
    160 DOMHighResTimeStamp Performance::TimeOrigin() {
    161  if (!mPerformanceService) {
    162    mPerformanceService = PerformanceService::GetOrCreate();
    163  }
    164 
    165  MOZ_ASSERT(mPerformanceService);
    166  DOMHighResTimeStamp rawTimeOrigin =
    167      mPerformanceService->TimeOrigin(CreationTimeStamp());
    168  // Time Origin is an absolute timestamp, so we supply a 0 context mix-in
    169  return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimeOrigin, 0,
    170                                                  mRTPCallerType);
    171 }
    172 
    173 JSObject* Performance::WrapObject(JSContext* aCx,
    174                                  JS::Handle<JSObject*> aGivenProto) {
    175  return Performance_Binding::Wrap(aCx, this, aGivenProto);
    176 }
    177 
    178 void Performance::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
    179  aRetval = mResourceEntries.Clone();
    180  aRetval.AppendElements(mUserEntries);
    181  aRetval.Sort(PerformanceEntryComparator());
    182 }
    183 
    184 void Performance::GetEntriesByType(
    185    const nsAString& aEntryType, nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
    186  RefPtr<nsAtom> entryType = NS_Atomize(aEntryType);
    187  if (entryType == nsGkAtoms::resource) {
    188    aRetval = mResourceEntries.Clone();
    189    return;
    190  }
    191 
    192  aRetval.Clear();
    193 
    194  if (entryType == nsGkAtoms::mark || entryType == nsGkAtoms::measure) {
    195    for (PerformanceEntry* entry : mUserEntries) {
    196      if (entry->GetEntryType() == entryType) {
    197        aRetval.AppendElement(entry);
    198      }
    199    }
    200  }
    201 }
    202 
    203 void Performance::GetEntriesByName(
    204    const nsAString& aName, const Optional<nsAString>& aEntryType,
    205    nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
    206  aRetval.Clear();
    207 
    208  RefPtr<nsAtom> name = NS_Atomize(aName);
    209  RefPtr<nsAtom> entryType =
    210      aEntryType.WasPassed() ? NS_Atomize(aEntryType.Value()) : nullptr;
    211 
    212  if (entryType) {
    213    if (entryType == nsGkAtoms::mark || entryType == nsGkAtoms::measure) {
    214      for (PerformanceEntry* entry : mUserEntries) {
    215        if (entry->GetName() == name && entry->GetEntryType() == entryType) {
    216          aRetval.AppendElement(entry);
    217        }
    218      }
    219      return;
    220    }
    221    if (entryType == nsGkAtoms::resource) {
    222      for (PerformanceEntry* entry : mResourceEntries) {
    223        MOZ_ASSERT(entry->GetEntryType() == entryType);
    224        if (entry->GetName() == name) {
    225          aRetval.AppendElement(entry);
    226        }
    227      }
    228      return;
    229    }
    230    // Invalid entryType
    231    return;
    232  }
    233 
    234  nsTArray<PerformanceEntry*> qualifiedResourceEntries;
    235  nsTArray<PerformanceEntry*> qualifiedUserEntries;
    236  // ::Measure expects that results from this function are already
    237  // passed through ReduceTimePrecision. mResourceEntries and mUserEntries
    238  // are, so the invariant holds.
    239  for (PerformanceEntry* entry : mResourceEntries) {
    240    if (entry->GetName() == name) {
    241      qualifiedResourceEntries.AppendElement(entry);
    242    }
    243  }
    244 
    245  for (PerformanceEntry* entry : mUserEntries) {
    246    if (entry->GetName() == name) {
    247      qualifiedUserEntries.AppendElement(entry);
    248    }
    249  }
    250 
    251  size_t resourceEntriesIdx = 0, userEntriesIdx = 0;
    252  aRetval.SetCapacity(qualifiedResourceEntries.Length() +
    253                      qualifiedUserEntries.Length());
    254 
    255  PerformanceEntryComparator comparator;
    256 
    257  while (resourceEntriesIdx < qualifiedResourceEntries.Length() &&
    258         userEntriesIdx < qualifiedUserEntries.Length()) {
    259    if (comparator.LessThan(qualifiedResourceEntries[resourceEntriesIdx],
    260                            qualifiedUserEntries[userEntriesIdx])) {
    261      aRetval.AppendElement(qualifiedResourceEntries[resourceEntriesIdx]);
    262      ++resourceEntriesIdx;
    263    } else {
    264      aRetval.AppendElement(qualifiedUserEntries[userEntriesIdx]);
    265      ++userEntriesIdx;
    266    }
    267  }
    268 
    269  while (resourceEntriesIdx < qualifiedResourceEntries.Length()) {
    270    aRetval.AppendElement(qualifiedResourceEntries[resourceEntriesIdx]);
    271    ++resourceEntriesIdx;
    272  }
    273 
    274  while (userEntriesIdx < qualifiedUserEntries.Length()) {
    275    aRetval.AppendElement(qualifiedUserEntries[userEntriesIdx]);
    276    ++userEntriesIdx;
    277  }
    278 }
    279 
    280 void Performance::GetEntriesByTypeForObserver(
    281    const nsAString& aEntryType, nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
    282  GetEntriesByType(aEntryType, aRetval);
    283 }
    284 
    285 void Performance::ClearUserEntries(const Optional<nsAString>& aEntryName,
    286                                   const nsAString& aEntryType) {
    287  MOZ_ASSERT(!aEntryType.IsEmpty());
    288  RefPtr<nsAtom> name =
    289      aEntryName.WasPassed() ? NS_Atomize(aEntryName.Value()) : nullptr;
    290  RefPtr<nsAtom> entryType = NS_Atomize(aEntryType);
    291  mUserEntries.RemoveElementsBy([name, entryType](const auto& entry) {
    292    return (!name || entry->GetName() == name) &&
    293           (entry->GetEntryType() == entryType);
    294  });
    295 }
    296 
    297 void Performance::ClearResourceTimings() { mResourceEntries.Clear(); }
    298 
    299 struct UserTimingMarker : public BaseMarkerType<UserTimingMarker> {
    300  static constexpr const char* Name = "UserTiming";
    301  static constexpr const char* Description =
    302      "UserTimingMeasure is created using the DOM API performance.measure().";
    303 
    304  using MS = MarkerSchema;
    305  static constexpr MS::PayloadField PayloadFields[] = {
    306      {"name", MS::InputType::String, "User Marker Name", MS::Format::String,
    307       MS::PayloadFlags::Searchable},
    308      {"entryType", MS::InputType::Boolean, "Entry Type"},
    309      {"startMark", MS::InputType::String, "Start Mark"},
    310      {"endMark", MS::InputType::String, "End Mark"}};
    311 
    312  static constexpr MS::Location Locations[] = {MS::Location::MarkerChart,
    313                                               MS::Location::MarkerTable};
    314  static constexpr const char* AllLabels = "{marker.data.name}";
    315 
    316  static constexpr MS::ETWMarkerGroup Group = MS::ETWMarkerGroup::UserMarkers;
    317 
    318  static void StreamJSONMarkerData(
    319      baseprofiler::SpliceableJSONWriter& aWriter,
    320      const ProfilerString16View& aName, bool aIsMeasure,
    321      const Maybe<ProfilerString16View>& aStartMark,
    322      const Maybe<ProfilerString16View>& aEndMark) {
    323    StreamJSONMarkerDataImpl(
    324        aWriter, aName,
    325        aIsMeasure ? MakeStringSpan("measure") : MakeStringSpan("mark"),
    326        aStartMark, aEndMark);
    327  }
    328 };
    329 
    330 already_AddRefed<PerformanceMark> Performance::Mark(
    331    JSContext* aCx, const nsAString& aName,
    332    const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv) {
    333  nsCOMPtr<nsIGlobalObject> parent = GetParentObject();
    334  if (!parent || parent->IsDying() || !parent->HasJSGlobal()) {
    335    aRv.ThrowInvalidStateError("Global object is unavailable");
    336    return nullptr;
    337  }
    338 
    339  GlobalObject global(aCx, parent->GetGlobalJSObject());
    340  if (global.Failed()) {
    341    aRv.ThrowInvalidStateError("Global object is unavailable");
    342    return nullptr;
    343  }
    344 
    345  RefPtr<PerformanceMark> performanceMark =
    346      PerformanceMark::Constructor(global, aName, aMarkOptions, aRv);
    347  if (aRv.Failed()) {
    348    return nullptr;
    349  }
    350 
    351  InsertUserEntry(performanceMark);
    352 
    353  if (profiler_thread_is_being_profiled_for_markers()) {
    354    Maybe<uint64_t> innerWindowId;
    355    if (nsGlobalWindowInner* owner = GetOwnerWindow()) {
    356      innerWindowId = Some(owner->WindowID());
    357    }
    358    TimeStamp startTimeStamp =
    359        CreationTimeStamp() +
    360        TimeDuration::FromMilliseconds(performanceMark->UnclampedStartTime());
    361    profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
    362                        MarkerOptions(MarkerTiming::InstantAt(startTimeStamp),
    363                                      MarkerInnerWindowId(innerWindowId)),
    364                        UserTimingMarker{}, aName, /* aIsMeasure */ false,
    365                        Nothing{}, Nothing{});
    366  }
    367 
    368  return performanceMark.forget();
    369 }
    370 
    371 void Performance::ClearMarks(const Optional<nsAString>& aName) {
    372  ClearUserEntries(aName, u"mark"_ns);
    373 }
    374 
    375 // To be removed once bug 1124165 lands
    376 bool Performance::IsPerformanceTimingAttribute(const nsAString& aName) const {
    377  // Note that toJSON is added to this list due to bug 1047848
    378  static const char* attributes[] = {"navigationStart",
    379                                     "unloadEventStart",
    380                                     "unloadEventEnd",
    381                                     "redirectStart",
    382                                     "redirectEnd",
    383                                     "fetchStart",
    384                                     "domainLookupStart",
    385                                     "domainLookupEnd",
    386                                     "connectStart",
    387                                     "secureConnectionStart",
    388                                     "connectEnd",
    389                                     "requestStart",
    390                                     "responseStart",
    391                                     "responseEnd",
    392                                     "domLoading",
    393                                     "domInteractive",
    394                                     "domContentLoadedEventStart",
    395                                     "domContentLoadedEventEnd",
    396                                     "domComplete",
    397                                     "loadEventStart",
    398                                     "loadEventEnd",
    399                                     nullptr};
    400 
    401  for (uint32_t i = 0; attributes[i]; ++i) {
    402    if (aName.EqualsASCII(attributes[i])) {
    403      return true;
    404    }
    405  }
    406 
    407  return false;
    408 }
    409 
    410 DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString(
    411    const nsAString& aName, ErrorResult& aRv, bool aReturnUnclamped) {
    412  if (IsPerformanceTimingAttribute(aName)) {
    413    return ConvertNameToTimestamp(aName, aRv);
    414  }
    415 
    416  RefPtr<nsAtom> name = NS_Atomize(aName);
    417  // Just loop over the user entries
    418  for (const PerformanceEntry* entry : Reversed(mUserEntries)) {
    419    if (entry->GetName() == name && entry->GetEntryType() == nsGkAtoms::mark) {
    420      if (aReturnUnclamped) {
    421        return entry->UnclampedStartTime();
    422      }
    423      return entry->StartTime();
    424    }
    425  }
    426 
    427  nsPrintfCString errorMsg("Given mark name, %s, is unknown",
    428                           NS_ConvertUTF16toUTF8(aName).get());
    429  aRv.ThrowSyntaxError(errorMsg);
    430  return 0;
    431 }
    432 
    433 DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp(
    434    const ResolveTimestampAttribute aAttribute,
    435    const DOMHighResTimeStamp aTimestamp, ErrorResult& aRv) {
    436  if (aTimestamp < 0) {
    437    nsAutoCString attributeName;
    438    switch (aAttribute) {
    439      case ResolveTimestampAttribute::Start:
    440        attributeName = "start";
    441        break;
    442      case ResolveTimestampAttribute::End:
    443        attributeName = "end";
    444        break;
    445      case ResolveTimestampAttribute::Duration:
    446        attributeName = "duration";
    447        break;
    448    }
    449 
    450    nsPrintfCString errorMsg("Given attribute %s cannot be negative",
    451                             attributeName.get());
    452    aRv.ThrowTypeError(errorMsg);
    453  }
    454  return aTimestamp;
    455 }
    456 
    457 DOMHighResTimeStamp Performance::ConvertMarkToTimestamp(
    458    const ResolveTimestampAttribute aAttribute,
    459    const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv,
    460    bool aReturnUnclamped) {
    461  if (aMarkNameOrTimestamp.IsString()) {
    462    return ConvertMarkToTimestampWithString(aMarkNameOrTimestamp.GetAsString(),
    463                                            aRv, aReturnUnclamped);
    464  }
    465 
    466  return ConvertMarkToTimestampWithDOMHighResTimeStamp(
    467      aAttribute, aMarkNameOrTimestamp.GetAsDouble(), aRv);
    468 }
    469 
    470 DOMHighResTimeStamp Performance::ConvertNameToTimestamp(const nsAString& aName,
    471                                                        ErrorResult& aRv) {
    472  if (!IsGlobalObjectWindow()) {
    473    nsPrintfCString errorMsg(
    474        "Cannot get PerformanceTiming attribute values for non-Window global "
    475        "object. Given: %s",
    476        NS_ConvertUTF16toUTF8(aName).get());
    477    aRv.ThrowTypeError(errorMsg);
    478    return 0;
    479  }
    480 
    481  if (aName.EqualsASCII("navigationStart")) {
    482    return 0;
    483  }
    484 
    485  // We use GetPerformanceTimingFromString, rather than calling the
    486  // navigationStart method timing function directly, because the former handles
    487  // reducing precision against timing attacks.
    488  const DOMHighResTimeStamp startTime =
    489      GetPerformanceTimingFromString(u"navigationStart"_ns);
    490  const DOMHighResTimeStamp endTime = GetPerformanceTimingFromString(aName);
    491  MOZ_ASSERT(endTime >= 0);
    492  if (endTime == 0) {
    493    nsPrintfCString errorMsg(
    494        "Given PerformanceTiming attribute, %s, isn't available yet",
    495        NS_ConvertUTF16toUTF8(aName).get());
    496    aRv.ThrowInvalidAccessError(errorMsg);
    497    return 0;
    498  }
    499 
    500  return endTime - startTime;
    501 }
    502 
    503 DOMHighResTimeStamp Performance::ResolveEndTimeForMeasure(
    504    const Optional<nsAString>& aEndMark,
    505    const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
    506    bool aReturnUnclamped) {
    507  DOMHighResTimeStamp endTime;
    508  if (aEndMark.WasPassed()) {
    509    endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv,
    510                                               aReturnUnclamped);
    511  } else if (aOptions && aOptions->mEnd.WasPassed()) {
    512    endTime =
    513        ConvertMarkToTimestamp(ResolveTimestampAttribute::End,
    514                               aOptions->mEnd.Value(), aRv, aReturnUnclamped);
    515  } else if (aOptions && aOptions->mStart.WasPassed() &&
    516             aOptions->mDuration.WasPassed()) {
    517    const DOMHighResTimeStamp start =
    518        ConvertMarkToTimestamp(ResolveTimestampAttribute::Start,
    519                               aOptions->mStart.Value(), aRv, aReturnUnclamped);
    520    if (aRv.Failed()) {
    521      return 0;
    522    }
    523 
    524    const DOMHighResTimeStamp duration =
    525        ConvertMarkToTimestampWithDOMHighResTimeStamp(
    526            ResolveTimestampAttribute::Duration, aOptions->mDuration.Value(),
    527            aRv);
    528    if (aRv.Failed()) {
    529      return 0;
    530    }
    531 
    532    endTime = start + duration;
    533  } else if (aReturnUnclamped) {
    534    MOZ_DIAGNOSTIC_ASSERT(sMarkerFile ||
    535                          profiler_thread_is_being_profiled_for_markers());
    536    endTime = NowUnclamped();
    537  } else {
    538    endTime = Now();
    539  }
    540 
    541  return endTime;
    542 }
    543 
    544 DOMHighResTimeStamp Performance::ResolveStartTimeForMeasure(
    545    const Maybe<const nsAString&>& aStartMark,
    546    const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
    547    bool aReturnUnclamped) {
    548  DOMHighResTimeStamp startTime;
    549  if (aOptions && aOptions->mStart.WasPassed()) {
    550    startTime =
    551        ConvertMarkToTimestamp(ResolveTimestampAttribute::Start,
    552                               aOptions->mStart.Value(), aRv, aReturnUnclamped);
    553  } else if (aOptions && aOptions->mDuration.WasPassed() &&
    554             aOptions->mEnd.WasPassed()) {
    555    const DOMHighResTimeStamp duration =
    556        ConvertMarkToTimestampWithDOMHighResTimeStamp(
    557            ResolveTimestampAttribute::Duration, aOptions->mDuration.Value(),
    558            aRv);
    559    if (aRv.Failed()) {
    560      return 0;
    561    }
    562 
    563    const DOMHighResTimeStamp end =
    564        ConvertMarkToTimestamp(ResolveTimestampAttribute::End,
    565                               aOptions->mEnd.Value(), aRv, aReturnUnclamped);
    566    if (aRv.Failed()) {
    567      return 0;
    568    }
    569 
    570    startTime = end - duration;
    571  } else if (aStartMark) {
    572    startTime =
    573        ConvertMarkToTimestampWithString(*aStartMark, aRv, aReturnUnclamped);
    574  } else {
    575    startTime = 0;
    576  }
    577 
    578  return startTime;
    579 }
    580 
    581 static std::string GetMarkerFilename() {
    582  std::stringstream s;
    583  if (char* markerDir = getenv("MOZ_PERFORMANCE_MARKER_DIR")) {
    584    s << markerDir << "/";
    585  }
    586 #ifdef XP_WIN
    587  s << "marker-" << GetCurrentProcessId() << ".txt";
    588 #else
    589  s << "marker-" << getpid() << ".txt";
    590 #endif
    591  return s.str();
    592 }
    593 
    594 Maybe<std::pair<TimeStamp, TimeStamp>> Performance::GetTimeStampsForMarker(
    595    const Maybe<const nsAString&>& aStartMark,
    596    const Optional<nsAString>& aEndMark,
    597    const Maybe<const PerformanceMeasureOptions&>& aOptions) {
    598  ErrorResult err;
    599  const DOMHighResTimeStamp unclampedStartTime = ResolveStartTimeForMeasure(
    600      aStartMark, aOptions, err, /* aReturnUnclamped */ true);
    601  const DOMHighResTimeStamp unclampedEndTime =
    602      ResolveEndTimeForMeasure(aEndMark, aOptions, /* aReturnUnclamped */
    603                               err, true);
    604 
    605  if (err.Failed()) {
    606    return Nothing();
    607  }
    608 
    609  // Performance.measure() can receive user-supplied timestamps and those
    610  // timestamps might not be relative to 'navigation start'. This is
    611  // (potentially) valid but, if we treat them as relative, we will end up
    612  // placing them far into the future which causes problems for the profiler
    613  // later so we report that as an error. (See bug 1925191 for details.)
    614  // kMaxFuture_ms represents approximately 10 years worth of milliseconds.
    615  static constexpr double kMaxFuture_ms = 31536000000.0;
    616  if (unclampedStartTime > kMaxFuture_ms || unclampedEndTime > kMaxFuture_ms) {
    617    return Nothing();
    618  }
    619 
    620  TimeStamp startTimeStamp =
    621      CreationTimeStamp() + TimeDuration::FromMilliseconds(unclampedStartTime);
    622  TimeStamp endTimeStamp =
    623      CreationTimeStamp() + TimeDuration::FromMilliseconds(unclampedEndTime);
    624 
    625  return Some(std::make_pair(startTimeStamp, endTimeStamp));
    626 }
    627 
    628 // Try to open the marker file for writing performance markers.
    629 // If successful, returns true and sMarkerFile will be defined
    630 // to the file handle.  Otherwise, return false and sMarkerFile
    631 // is NULL.
    632 static bool MaybeOpenMarkerFile() {
    633  if (!getenv("MOZ_USE_PERFORMANCE_MARKER_FILE")) {
    634    return false;
    635  }
    636 
    637  // Check if it's already open.
    638  if (sMarkerFile) {
    639    return true;
    640  }
    641 
    642 #ifdef XP_LINUX
    643  // We treat marker files similar to Jitdump files (see PerfSpewer.cpp) and
    644  // mmap them if needed.
    645  int fd = open(GetMarkerFilename().c_str(), O_CREAT | O_TRUNC | O_RDWR, 0666);
    646  sMarkerFile = fdopen(fd, "w+");
    647  if (!sMarkerFile) {
    648    return false;
    649  }
    650 
    651  // On Linux and Android, we need to mmap the file so that the path makes it
    652  // into the perf.data file or into samply.
    653  // On non-Android, make the mapping executable, otherwise the MMAP event may
    654  // not be recorded by perf (see perf_event_open mmap_data).
    655  // But on Android, don't make the mapping executable, because doing so can
    656  // make the mmap call fail on some Android devices. It's also not required on
    657  // Android because simpleperf sets mmap_data = 1 for unrelated reasons (it
    658  // wants to know about vdex files for Java JIT profiling, see
    659  // SetRecordNotExecutableMaps).
    660  int protection = PROT_READ;
    661 #  ifndef ANDROID
    662  protection |= PROT_EXEC;
    663 #  endif
    664 
    665  // Mmap just the first page - that's enough to ensure the path makes it into
    666  // the recording.
    667  long page_size = sysconf(_SC_PAGESIZE);
    668  void* mmap_address = mmap(nullptr, page_size, protection, MAP_PRIVATE, fd, 0);
    669  if (mmap_address == MAP_FAILED) {
    670    fclose(sMarkerFile);
    671    sMarkerFile = nullptr;
    672    return false;
    673  }
    674 #else
    675  // On macOS, we just need to `open` or `fopen` the marker file, and samply
    676  // will know its path because it hooks those functions - no mmap needed.
    677  // On Windows, there's no need to use MOZ_USE_PERFORMANCE_MARKER_FILE because
    678  // we have ETW trace events for UserTiming measures. Still, we want this code
    679  // to compile successfully on Windows, so we use fopen rather than
    680  // open+fdopen.
    681  sMarkerFile = fopen(GetMarkerFilename().c_str(), "w+");
    682  if (!sMarkerFile) {
    683    return false;
    684  }
    685 #endif
    686  return true;
    687 }
    688 
    689 // This emits markers to an external marker-[pid].txt file for use by an
    690 // external profiler like samply or etw-gecko
    691 void Performance::MaybeEmitExternalProfilerMarker(
    692    const nsAString& aName, Maybe<const PerformanceMeasureOptions&> aOptions,
    693    Maybe<const nsAString&> aStartMark, const Optional<nsAString>& aEndMark) {
    694  if (!MaybeOpenMarkerFile()) {
    695    return;
    696  }
    697 
    698 #if defined(XP_LINUX) || defined(XP_WIN) || defined(XP_MACOSX)
    699  Maybe<std::pair<TimeStamp, TimeStamp>> tsPair =
    700      GetTimeStampsForMarker(aStartMark, aEndMark, aOptions);
    701  if (tsPair.isNothing()) {
    702    return;
    703  }
    704  auto [startTimeStamp, endTimeStamp] = tsPair.value();
    705 #endif
    706 
    707 #ifdef XP_LINUX
    708  uint64_t rawStart = startTimeStamp.RawClockMonotonicNanosecondsSinceBoot();
    709  uint64_t rawEnd = endTimeStamp.RawClockMonotonicNanosecondsSinceBoot();
    710 #elif XP_WIN
    711  uint64_t rawStart = startTimeStamp.RawQueryPerformanceCounterValue();
    712  uint64_t rawEnd = endTimeStamp.RawQueryPerformanceCounterValue();
    713 #elif XP_MACOSX
    714  uint64_t rawStart = startTimeStamp.RawMachAbsoluteTimeNanoseconds();
    715  uint64_t rawEnd = endTimeStamp.RawMachAbsoluteTimeNanoseconds();
    716 #else
    717  uint64_t rawStart = 0;
    718  uint64_t rawEnd = 0;
    719  MOZ_CRASH("no timestamp");
    720 #endif
    721  // Write a line for this measure to the marker file. The marker file uses a
    722  // text-based format where every line is one marker, and each line has the
    723  // format:
    724  // `<raw_start_timestamp> <raw_end_timestamp> <measure_name>`
    725  //
    726  // The timestamp value is OS specific.
    727  fprintf(sMarkerFile, "%" PRIu64 " %" PRIu64 " %s\n", rawStart, rawEnd,
    728          NS_ConvertUTF16toUTF8(aName).get());
    729  fflush(sMarkerFile);
    730 }
    731 
    732 void MOZ_ALWAYS_INLINE Performance::MaybeAddProfileMarker(
    733    const nsAString& aName,
    734    const Maybe<const PerformanceMeasureOptions&>& options,
    735    const Maybe<const nsAString&>& aStartMark,
    736    const Optional<nsAString>& aEndMark) {
    737  if (profiler_thread_is_being_profiled_for_markers()) {
    738    AddProfileMarker(aName, options, aStartMark, aEndMark);
    739  }
    740 }
    741 
    742 void MOZ_NEVER_INLINE Performance::AddProfileMarker(
    743    const nsAString& aName,
    744    const Maybe<const PerformanceMeasureOptions&>& options,
    745    const Maybe<const nsAString&>& aStartMark,
    746    const Optional<nsAString>& aEndMark) {
    747  Maybe<std::pair<TimeStamp, TimeStamp>> tsPair =
    748      GetTimeStampsForMarker(aStartMark, aEndMark, options);
    749  if (tsPair.isNothing()) {
    750    return;
    751  }
    752  auto [startTimeStamp, endTimeStamp] = tsPair.value();
    753 
    754  Maybe<nsString> endMark;
    755  if (aEndMark.WasPassed()) {
    756    endMark.emplace(aEndMark.Value());
    757  }
    758 
    759  Maybe<uint64_t> innerWindowId;
    760  if (nsGlobalWindowInner* owner = GetOwnerWindow()) {
    761    innerWindowId = Some(owner->WindowID());
    762  }
    763  profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
    764                      {MarkerTiming::Interval(startTimeStamp, endTimeStamp),
    765                       MarkerInnerWindowId(innerWindowId)},
    766                      UserTimingMarker{}, aName, /* aIsMeasure */ true,
    767                      aStartMark, endMark);
    768 }
    769 
    770 already_AddRefed<PerformanceMeasure> Performance::Measure(
    771    JSContext* aCx, const nsAString& aName,
    772    const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions,
    773    const Optional<nsAString>& aEndMark, ErrorResult& aRv) {
    774  if (!GetParentObject()) {
    775    aRv.ThrowInvalidStateError("Global object is unavailable");
    776    return nullptr;
    777  }
    778 
    779  // Maybe is more readable than using the union type directly.
    780  Maybe<const PerformanceMeasureOptions&> options;
    781  if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) {
    782    options.emplace(aStartOrMeasureOptions.GetAsPerformanceMeasureOptions());
    783  }
    784 
    785  const bool isOptionsNotEmpty =
    786      options.isSome() &&
    787      (!options->mDetail.isUndefined() || options->mStart.WasPassed() ||
    788       options->mEnd.WasPassed() || options->mDuration.WasPassed());
    789  if (isOptionsNotEmpty) {
    790    if (aEndMark.WasPassed()) {
    791      aRv.ThrowTypeError(
    792          "Cannot provide separate endMark argument if "
    793          "PerformanceMeasureOptions argument is given");
    794      return nullptr;
    795    }
    796 
    797    if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) {
    798      aRv.ThrowTypeError(
    799          "PerformanceMeasureOptions must have start and/or end member");
    800      return nullptr;
    801    }
    802 
    803    if (options->mStart.WasPassed() && options->mDuration.WasPassed() &&
    804        options->mEnd.WasPassed()) {
    805      aRv.ThrowTypeError(
    806          "PerformanceMeasureOptions cannot have all of the following members: "
    807          "start, duration, and end");
    808      return nullptr;
    809    }
    810  }
    811 
    812  const DOMHighResTimeStamp endTime = ResolveEndTimeForMeasure(
    813      aEndMark, options, aRv, /* aReturnUnclamped */ false);
    814  if (NS_WARN_IF(aRv.Failed())) {
    815    return nullptr;
    816  }
    817 
    818  // Convert to Maybe for consistency with options.
    819  Maybe<const nsAString&> startMark;
    820  if (aStartOrMeasureOptions.IsString()) {
    821    startMark.emplace(aStartOrMeasureOptions.GetAsString());
    822  }
    823  const DOMHighResTimeStamp startTime = ResolveStartTimeForMeasure(
    824      startMark, options, aRv, /* aReturnUnclamped */ false);
    825  if (NS_WARN_IF(aRv.Failed())) {
    826    return nullptr;
    827  }
    828 
    829  JS::Rooted<JS::Value> detail(aCx);
    830  if (options && !options->mDetail.isNullOrUndefined()) {
    831    StructuredSerializeOptions serializeOptions;
    832    JS::Rooted<JS::Value> valueToClone(aCx, options->mDetail);
    833    nsContentUtils::StructuredClone(aCx, GetParentObject(), valueToClone,
    834                                    serializeOptions, &detail, aRv);
    835    if (aRv.Failed()) {
    836      return nullptr;
    837    }
    838  } else {
    839    detail.setNull();
    840  }
    841 
    842 #ifdef MOZ_PERFETTO
    843  // Perfetto requires that events are properly nested within each category.
    844  // Since this is not a guarantee here, we need to define a dynamic category
    845  // for each measurement so it's not prematurely ended by another measurement
    846  // that overlaps.  We also use the usertiming category to guard these markers
    847  // so it's easy to toggle.
    848  if (TRACE_EVENT_CATEGORY_ENABLED("usertiming")) {
    849    NS_ConvertUTF16toUTF8 str(aName);
    850    perfetto::DynamicCategory category{str.get()};
    851    TimeStamp startTimeStamp =
    852        CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime);
    853    TimeStamp endTimeStamp =
    854        CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime);
    855    PERFETTO_TRACE_EVENT_BEGIN(category, perfetto::DynamicString{str.get()},
    856                               startTimeStamp);
    857    PERFETTO_TRACE_EVENT_END(category, endTimeStamp);
    858  }
    859 #endif
    860 
    861  RefPtr<PerformanceMeasure> performanceMeasure = new PerformanceMeasure(
    862      GetParentObject(), aName, startTime, endTime, detail);
    863  InsertUserEntry(performanceMeasure);
    864 
    865  MaybeEmitExternalProfilerMarker(aName, options, startMark, aEndMark);
    866 
    867  MaybeAddProfileMarker(aName, options, startMark, aEndMark);
    868 
    869  return performanceMeasure.forget();
    870 }
    871 
    872 void Performance::ClearMeasures(const Optional<nsAString>& aName) {
    873  ClearUserEntries(aName, u"measure"_ns);
    874 }
    875 
    876 void Performance::LogEntry(PerformanceEntry* aEntry,
    877                           const nsACString& aOwner) const {
    878  PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
    879          aOwner.BeginReading(),
    880          NS_ConvertUTF16toUTF8(aEntry->GetEntryType()->GetUTF16String()).get(),
    881          NS_ConvertUTF16toUTF8(aEntry->GetName()->GetUTF16String()).get(),
    882          aEntry->StartTime(), aEntry->Duration(),
    883          static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
    884 }
    885 
    886 void Performance::TimingNotification(PerformanceEntry* aEntry,
    887                                     const nsACString& aOwner,
    888                                     const double aEpoch) {
    889  PerformanceEntryEventInit init;
    890  init.mBubbles = false;
    891  init.mCancelable = false;
    892  aEntry->GetName(init.mName);
    893  aEntry->GetEntryType(init.mEntryType);
    894  init.mStartTime = aEntry->StartTime();
    895  init.mDuration = aEntry->Duration();
    896  init.mEpoch = aEpoch;
    897  CopyUTF8toUTF16(aOwner, init.mOrigin);
    898 
    899  RefPtr<PerformanceEntryEvent> perfEntryEvent =
    900      PerformanceEntryEvent::Constructor(this, u"performanceentry"_ns, init);
    901  if (RefPtr<nsGlobalWindowInner> owner = GetOwnerWindow()) {
    902    owner->DispatchEvent(*perfEntryEvent);
    903  }
    904 }
    905 
    906 void Performance::InsertUserEntry(PerformanceEntry* aEntry) {
    907  mUserEntries.InsertElementSorted(aEntry, PerformanceEntryComparator());
    908 
    909  QueueEntry(aEntry);
    910 }
    911 
    912 /*
    913 * Steps are labeled according to the description found at
    914 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
    915 *
    916 * Buffer Full Event
    917 */
    918 void Performance::ResourceTimingBufferFullEvent() {
    919  /*
    920   * While resource timing secondary buffer is not empty,
    921   * run the following substeps:
    922   */
    923  while (!mSecondaryResourceEntries.IsEmpty()) {
    924    uint32_t secondaryResourceEntriesBeforeCount = 0;
    925    uint32_t secondaryResourceEntriesAfterCount = 0;
    926 
    927    /*
    928     * Let number of excess entries before be resource
    929     * timing secondary buffer current size.
    930     */
    931    secondaryResourceEntriesBeforeCount = mSecondaryResourceEntries.Length();
    932 
    933    /*
    934     * If can add resource timing entry returns false,
    935     * then fire an event named resourcetimingbufferfull
    936     * at the Performance object.
    937     */
    938    if (!CanAddResourceTimingEntry()) {
    939      DispatchResourceTimingBufferFullEvent();
    940    }
    941 
    942    /*
    943     * Run copy secondary buffer.
    944     *
    945     * While resource timing secondary buffer is not
    946     * empty and can add resource timing entry returns
    947     * true ...
    948     */
    949    while (!mSecondaryResourceEntries.IsEmpty() &&
    950           CanAddResourceTimingEntry()) {
    951      /*
    952       * Let entry be the oldest PerformanceResourceTiming
    953       * in resource timing secondary buffer. Add entry to
    954       * the end of performance entry buffer. Increment
    955       * resource timing buffer current size by 1.
    956       */
    957      mResourceEntries.InsertElementSorted(
    958          mSecondaryResourceEntries.ElementAt(0), PerformanceEntryComparator());
    959      /*
    960       * Remove entry from resource timing secondary buffer.
    961       * Decrement resource timing secondary buffer current
    962       * size by 1.
    963       */
    964      mSecondaryResourceEntries.RemoveElementAt(0);
    965    }
    966 
    967    /*
    968     * Let number of excess entries after be resource
    969     * timing secondary buffer current size.
    970     */
    971    secondaryResourceEntriesAfterCount = mSecondaryResourceEntries.Length();
    972 
    973    /*
    974     * If number of excess entries before is lower than
    975     * or equals number of excess entries after, then
    976     * remove all entries from resource timing secondary
    977     * buffer, set resource timing secondary buffer current
    978     * size to 0, and abort these steps.
    979     */
    980    if (secondaryResourceEntriesBeforeCount <=
    981        secondaryResourceEntriesAfterCount) {
    982      mSecondaryResourceEntries.Clear();
    983      break;
    984    }
    985  }
    986  /*
    987   * Set resource timing buffer full event pending flag
    988   * to false.
    989   */
    990  mPendingResourceTimingBufferFullEvent = false;
    991 }
    992 
    993 void Performance::SetResourceTimingBufferSize(uint64_t aMaxSize) {
    994  mResourceTimingBufferSize = aMaxSize;
    995 }
    996 
    997 /*
    998 * Steps are labeled according to the description found at
    999 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
   1000 *
   1001 * Can Add Resource Timing Entry
   1002 */
   1003 MOZ_ALWAYS_INLINE bool Performance::CanAddResourceTimingEntry() {
   1004  /*
   1005   * If resource timing buffer current size is smaller than resource timing
   1006   * buffer size limit, return true. [Otherwise,] [r]eturn false.
   1007   */
   1008  return mResourceEntries.Length() < mResourceTimingBufferSize;
   1009 }
   1010 
   1011 /*
   1012 * Steps are labeled according to the description found at
   1013 * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface.
   1014 *
   1015 * Add a PerformanceResourceTiming Entry
   1016 */
   1017 void Performance::InsertResourceEntry(PerformanceEntry* aEntry) {
   1018  MOZ_ASSERT(aEntry);
   1019 
   1020  QueueEntry(aEntry);
   1021 
   1022  /*
   1023   * Let new entry be the input PerformanceEntry to be added.
   1024   *
   1025   * If can add resource timing entry returns true and resource
   1026   * timing buffer full event pending flag is false ...
   1027   */
   1028  if (CanAddResourceTimingEntry() && !mPendingResourceTimingBufferFullEvent) {
   1029    /*
   1030     * Add new entry to the performance entry buffer.
   1031     * Increase resource timing buffer current size by 1.
   1032     */
   1033    mResourceEntries.InsertElementSorted(aEntry, PerformanceEntryComparator());
   1034    return;
   1035  }
   1036 
   1037  /*
   1038   * If resource timing buffer full event pending flag is
   1039   * false ...
   1040   */
   1041  if (!mPendingResourceTimingBufferFullEvent) {
   1042    /*
   1043     * Set resource timing buffer full event pending flag
   1044     * to true.
   1045     */
   1046    mPendingResourceTimingBufferFullEvent = true;
   1047 
   1048    /*
   1049     * Queue a task to run fire a buffer full event.
   1050     */
   1051    NS_DispatchToCurrentThread(NewCancelableRunnableMethod(
   1052        "Performance::ResourceTimingBufferFullEvent", this,
   1053        &Performance::ResourceTimingBufferFullEvent));
   1054  }
   1055  /*
   1056   * Add new entry to the resource timing secondary buffer.
   1057   * Increase resource timing secondary buffer current size
   1058   * by 1.
   1059   */
   1060  mSecondaryResourceEntries.InsertElementSorted(aEntry,
   1061                                                PerformanceEntryComparator());
   1062 }
   1063 
   1064 void Performance::AddObserver(PerformanceObserver* aObserver) {
   1065  mObservers.AppendElementUnlessExists(aObserver);
   1066 }
   1067 
   1068 void Performance::RemoveObserver(PerformanceObserver* aObserver) {
   1069  mObservers.RemoveElement(aObserver);
   1070 }
   1071 
   1072 void Performance::NotifyObservers() {
   1073  mPendingNotificationObserversTask = false;
   1074  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, Notify, ());
   1075 }
   1076 
   1077 void Performance::CancelNotificationObservers() {
   1078  mPendingNotificationObserversTask = false;
   1079 }
   1080 
   1081 class NotifyObserversTask final : public CancelableRunnable {
   1082 public:
   1083  explicit NotifyObserversTask(Performance* aPerformance)
   1084      : CancelableRunnable("dom::NotifyObserversTask"),
   1085        mPerformance(aPerformance) {
   1086    MOZ_ASSERT(mPerformance);
   1087  }
   1088 
   1089  // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now until Runnable::Run is
   1090  // MOZ_CAN_RUN_SCRIPT.
   1091  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   1092  NS_IMETHOD Run() override {
   1093    MOZ_ASSERT(mPerformance);
   1094    RefPtr<Performance> performance(mPerformance);
   1095    performance->NotifyObservers();
   1096    return NS_OK;
   1097  }
   1098 
   1099  nsresult Cancel() override {
   1100    mPerformance->CancelNotificationObservers();
   1101    mPerformance = nullptr;
   1102    return NS_OK;
   1103  }
   1104 
   1105 private:
   1106  ~NotifyObserversTask() = default;
   1107 
   1108  RefPtr<Performance> mPerformance;
   1109 };
   1110 
   1111 void Performance::QueueNotificationObserversTask() {
   1112  if (!mPendingNotificationObserversTask) {
   1113    RunNotificationObserversTask();
   1114  }
   1115 }
   1116 
   1117 void Performance::RunNotificationObserversTask() {
   1118  mPendingNotificationObserversTask = true;
   1119  nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this);
   1120  nsresult rv;
   1121  if (nsIGlobalObject* global = GetOwnerGlobal()) {
   1122    rv = global->Dispatch(task.forget());
   1123  } else {
   1124    rv = NS_DispatchToCurrentThread(task.forget());
   1125  }
   1126  if (NS_WARN_IF(NS_FAILED(rv))) {
   1127    mPendingNotificationObserversTask = false;
   1128  }
   1129 }
   1130 
   1131 void Performance::QueueEntry(PerformanceEntry* aEntry) {
   1132  nsTObserverArray<PerformanceObserver*> interestedObservers;
   1133  if (!mObservers.IsEmpty()) {
   1134    const auto [begin, end] = mObservers.NonObservingRange();
   1135    std::copy_if(begin, end, MakeBackInserter(interestedObservers),
   1136                 [&](PerformanceObserver* observer) {
   1137                   return observer->ObservesTypeOfEntry(aEntry);
   1138                 });
   1139  }
   1140 
   1141  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(interestedObservers, QueueEntry,
   1142                                           (aEntry));
   1143 
   1144  aEntry->BufferEntryIfNeeded();
   1145 
   1146  if (!interestedObservers.IsEmpty()) {
   1147    QueueNotificationObserversTask();
   1148  }
   1149 }
   1150 
   1151 // We could clear User entries here, but doing so could break sites that call
   1152 // performance.measure() if the marks disappeared without warning.   Chrome
   1153 // allows "infinite" entries.
   1154 void Performance::MemoryPressure() {}
   1155 
   1156 size_t Performance::SizeOfUserEntries(
   1157    mozilla::MallocSizeOf aMallocSizeOf) const {
   1158  size_t userEntries = 0;
   1159  for (const PerformanceEntry* entry : mUserEntries) {
   1160    userEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
   1161  }
   1162  return userEntries;
   1163 }
   1164 
   1165 size_t Performance::SizeOfResourceEntries(
   1166    mozilla::MallocSizeOf aMallocSizeOf) const {
   1167  size_t resourceEntries = 0;
   1168  for (const PerformanceEntry* entry : mResourceEntries) {
   1169    resourceEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
   1170  }
   1171  return resourceEntries;
   1172 }
   1173 
   1174 }  // namespace mozilla::dom