forked from mirrors/gecko-dev
The converted value gets passed into TimingNotification which assigns the value into a PerformanceEntry (this is the only usage). Since PerformanceEntry is defined in WebIDL, we could not change its types (which is double for mEpoch) so we were forced to safely convert the value where we did. I don't think the existing conversion code had any bugs since we converted a 64-bit signed integer timestamp into uint64_t (safe) into a double (which is safe for the reasons mentioned in the code comments). Differential Revision: https://phabricator.services.mozilla.com/D145142
627 lines
20 KiB
C++
627 lines
20 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "PerformanceMainThread.h"
|
|
#include "PerformanceNavigation.h"
|
|
#include "PerformancePaintTiming.h"
|
|
#include "jsapi.h"
|
|
#include "js/GCAPI.h"
|
|
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
|
#include "mozilla/HoldDropJSObjects.h"
|
|
#include "PerformanceEventTiming.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "mozilla/dom/EventCounts.h"
|
|
#include "mozilla/dom/PerformanceEventTimingBinding.h"
|
|
#include "mozilla/dom/PerformanceNavigationTiming.h"
|
|
#include "mozilla/dom/PerformanceResourceTiming.h"
|
|
#include "mozilla/dom/PerformanceTiming.h"
|
|
#include "mozilla/StaticPrefs_dom.h"
|
|
#include "mozilla/StaticPrefs_privacy.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsIHttpChannel.h"
|
|
#include "nsIDocShell.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
namespace {
|
|
|
|
void GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec) {
|
|
aSpec.AssignLiteral("document");
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aChannel);
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = channel->GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString spec;
|
|
rv = uri->GetSpec(spec);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
CopyUTF8toUTF16(spec, aSpec);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
|
|
Performance)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry, mFCPTiming,
|
|
mEventTimingEntries, mFirstInputEvent,
|
|
mPendingPointerDown,
|
|
mPendingEventTimingEntries, mEventCounts)
|
|
mozilla::DropJSObjects(tmp);
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
|
|
Performance)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming, mNavigation, mDocEntry, mFCPTiming,
|
|
mEventTimingEntries, mFirstInputEvent,
|
|
mPendingPointerDown,
|
|
mPendingEventTimingEntries, mEventCounts)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
|
|
Performance)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
|
|
NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
|
|
|
|
// QueryInterface implementation for PerformanceMainThread
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END_INHERITING(Performance)
|
|
|
|
PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
|
|
nsDOMNavigationTiming* aDOMTiming,
|
|
nsITimedChannel* aChannel,
|
|
bool aPrincipal)
|
|
: Performance(aWindow, aPrincipal),
|
|
mDOMTiming(aDOMTiming),
|
|
mChannel(aChannel),
|
|
mCrossOriginIsolated(aWindow->AsGlobal()->CrossOriginIsolated()) {
|
|
MOZ_ASSERT(aWindow, "Parent window object should be provided");
|
|
if (StaticPrefs::dom_enable_event_timing()) {
|
|
mEventCounts = new class EventCounts(GetParentObject());
|
|
}
|
|
CreateNavigationTimingEntry();
|
|
}
|
|
|
|
PerformanceMainThread::~PerformanceMainThread() {
|
|
mozilla::DropJSObjects(this);
|
|
}
|
|
|
|
void PerformanceMainThread::GetMozMemory(JSContext* aCx,
|
|
JS::MutableHandle<JSObject*> aObj) {
|
|
if (!mMozMemory) {
|
|
JS::Rooted<JSObject*> mozMemoryObj(aCx, JS_NewPlainObject(aCx));
|
|
JS::Rooted<JSObject*> gcMemoryObj(aCx, js::gc::NewMemoryInfoObject(aCx));
|
|
if (!mozMemoryObj || !gcMemoryObj) {
|
|
MOZ_CRASH("out of memory creating performance.mozMemory");
|
|
}
|
|
if (!JS_DefineProperty(aCx, mozMemoryObj, "gc", gcMemoryObj,
|
|
JSPROP_ENUMERATE)) {
|
|
MOZ_CRASH("out of memory creating performance.mozMemory");
|
|
}
|
|
mMozMemory = mozMemoryObj;
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
|
|
aObj.set(mMozMemory);
|
|
}
|
|
|
|
PerformanceTiming* PerformanceMainThread::Timing() {
|
|
if (!mTiming) {
|
|
// For navigation timing, the third argument (an nsIHttpChannel) is null
|
|
// since the cross-domain redirect were already checked. The last
|
|
// argument (zero time) for performance.timing is the navigation start
|
|
// value.
|
|
mTiming = new PerformanceTiming(this, mChannel, nullptr,
|
|
mDOMTiming->GetNavigationStart());
|
|
}
|
|
|
|
return mTiming;
|
|
}
|
|
|
|
void PerformanceMainThread::DispatchBufferFullEvent() {
|
|
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
|
// it bubbles, and it isn't cancelable
|
|
event->InitEvent(u"resourcetimingbufferfull"_ns, true, false);
|
|
event->SetTrusted(true);
|
|
DispatchEvent(*event);
|
|
}
|
|
|
|
PerformanceNavigation* PerformanceMainThread::Navigation() {
|
|
if (!mNavigation) {
|
|
mNavigation = new PerformanceNavigation(this);
|
|
}
|
|
|
|
return mNavigation;
|
|
}
|
|
|
|
/**
|
|
* An entry should be added only after the resource is loaded.
|
|
* This method is not thread safe and can only be called on the main thread.
|
|
*/
|
|
void PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
|
|
nsITimedChannel* timedChannel) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsAutoString initiatorType;
|
|
nsAutoString entryName;
|
|
|
|
UniquePtr<PerformanceTimingData> performanceTimingData(
|
|
PerformanceTimingData::Create(timedChannel, channel, 0, initiatorType,
|
|
entryName));
|
|
if (!performanceTimingData) {
|
|
return;
|
|
}
|
|
AddRawEntry(std::move(performanceTimingData), initiatorType, entryName);
|
|
}
|
|
|
|
void PerformanceMainThread::AddEntry(const nsString& entryName,
|
|
const nsString& initiatorType,
|
|
UniquePtr<PerformanceTimingData>&& aData) {
|
|
AddRawEntry(std::move(aData), initiatorType, entryName);
|
|
}
|
|
|
|
void PerformanceMainThread::AddRawEntry(UniquePtr<PerformanceTimingData> aData,
|
|
const nsAString& aInitiatorType,
|
|
const nsAString& aEntryName) {
|
|
// The PerformanceResourceTiming object will use the PerformanceTimingData
|
|
// object to get all the required timings.
|
|
auto entry =
|
|
MakeRefPtr<PerformanceResourceTiming>(std::move(aData), this, aEntryName);
|
|
entry->SetInitiatorType(aInitiatorType);
|
|
InsertResourceEntry(entry);
|
|
}
|
|
|
|
void PerformanceMainThread::SetFCPTimingEntry(PerformancePaintTiming* aEntry) {
|
|
MOZ_ASSERT(aEntry);
|
|
if (!mFCPTiming) {
|
|
mFCPTiming = aEntry;
|
|
QueueEntry(aEntry);
|
|
}
|
|
}
|
|
|
|
void PerformanceMainThread::InsertEventTimingEntry(
|
|
PerformanceEventTiming* aEventEntry) {
|
|
mPendingEventTimingEntries.insertBack(aEventEntry);
|
|
|
|
if (mHasQueuedRefreshdriverObserver) {
|
|
return;
|
|
}
|
|
|
|
PresShell* presShell = GetPresShell();
|
|
if (!presShell) {
|
|
return;
|
|
}
|
|
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
if (!presContext) {
|
|
return;
|
|
}
|
|
|
|
// Using PostRefreshObserver is fine because we don't
|
|
// run any JS between the `mark paint timing` step and the
|
|
// `pending Event Timing entries` step. So mixing the order
|
|
// here is fine.
|
|
mHasQueuedRefreshdriverObserver = true;
|
|
presContext->RegisterManagedPostRefreshObserver(
|
|
new ManagedPostRefreshObserver(
|
|
presContext, [performance = RefPtr<PerformanceMainThread>(this)](
|
|
bool aWasCanceled) {
|
|
if (!aWasCanceled) {
|
|
// XXX Should we do this even if canceled?
|
|
performance->DispatchPendingEventTimingEntries();
|
|
}
|
|
performance->mHasQueuedRefreshdriverObserver = false;
|
|
return ManagedPostRefreshObserver::Unregister::Yes;
|
|
}));
|
|
}
|
|
|
|
void PerformanceMainThread::BufferEventTimingEntryIfNeeded(
|
|
PerformanceEventTiming* aEventEntry) {
|
|
if (mEventTimingEntries.Length() < kDefaultEventTimingBufferSize) {
|
|
mEventTimingEntries.AppendElement(aEventEntry);
|
|
}
|
|
}
|
|
|
|
void PerformanceMainThread::DispatchPendingEventTimingEntries() {
|
|
DOMHighResTimeStamp renderingTime = NowUnclamped();
|
|
|
|
while (!mPendingEventTimingEntries.isEmpty()) {
|
|
RefPtr<PerformanceEventTiming> entry =
|
|
mPendingEventTimingEntries.popFirst();
|
|
|
|
entry->SetDuration(renderingTime - entry->RawStartTime());
|
|
IncEventCount(entry->GetName());
|
|
|
|
if (entry->RawDuration() >= kDefaultEventTimingMinDuration) {
|
|
QueueEntry(entry);
|
|
}
|
|
|
|
if (!mHasDispatchedInputEvent) {
|
|
switch (entry->GetMessage()) {
|
|
case ePointerDown: {
|
|
mPendingPointerDown = entry->Clone();
|
|
mPendingPointerDown->SetEntryType(u"first-input"_ns);
|
|
break;
|
|
}
|
|
case ePointerUp: {
|
|
if (mPendingPointerDown) {
|
|
MOZ_ASSERT(!mFirstInputEvent);
|
|
mFirstInputEvent = mPendingPointerDown.forget();
|
|
QueueEntry(mFirstInputEvent);
|
|
mHasDispatchedInputEvent = true;
|
|
}
|
|
break;
|
|
}
|
|
case eMouseClick:
|
|
case eKeyDown:
|
|
case eMouseDown: {
|
|
mFirstInputEvent = entry->Clone();
|
|
mFirstInputEvent->SetEntryType(u"first-input"_ns);
|
|
QueueEntry(mFirstInputEvent);
|
|
mHasDispatchedInputEvent = true;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// To be removed once bug 1124165 lands
|
|
bool PerformanceMainThread::IsPerformanceTimingAttribute(
|
|
const nsAString& aName) {
|
|
// Note that toJSON is added to this list due to bug 1047848
|
|
static const char* attributes[] = {"navigationStart",
|
|
"unloadEventStart",
|
|
"unloadEventEnd",
|
|
"redirectStart",
|
|
"redirectEnd",
|
|
"fetchStart",
|
|
"domainLookupStart",
|
|
"domainLookupEnd",
|
|
"connectStart",
|
|
"secureConnectionStart",
|
|
"connectEnd",
|
|
"requestStart",
|
|
"responseStart",
|
|
"responseEnd",
|
|
"domLoading",
|
|
"domInteractive",
|
|
"domContentLoadedEventStart",
|
|
"domContentLoadedEventEnd",
|
|
"domComplete",
|
|
"loadEventStart",
|
|
"loadEventEnd",
|
|
nullptr};
|
|
|
|
for (uint32_t i = 0; attributes[i]; ++i) {
|
|
if (aName.EqualsASCII(attributes[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DOMHighResTimeStamp PerformanceMainThread::GetPerformanceTimingFromString(
|
|
const nsAString& aProperty) {
|
|
// ::Measure expects the values returned from this function to be passed
|
|
// through ReduceTimePrecision already.
|
|
if (!IsPerformanceTimingAttribute(aProperty)) {
|
|
return 0;
|
|
}
|
|
// Values from Timing() are already reduced
|
|
if (aProperty.EqualsLiteral("redirectStart")) {
|
|
return Timing()->RedirectStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("redirectEnd")) {
|
|
return Timing()->RedirectEnd();
|
|
}
|
|
if (aProperty.EqualsLiteral("fetchStart")) {
|
|
return Timing()->FetchStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("domainLookupStart")) {
|
|
return Timing()->DomainLookupStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("domainLookupEnd")) {
|
|
return Timing()->DomainLookupEnd();
|
|
}
|
|
if (aProperty.EqualsLiteral("connectStart")) {
|
|
return Timing()->ConnectStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("secureConnectionStart")) {
|
|
return Timing()->SecureConnectionStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("connectEnd")) {
|
|
return Timing()->ConnectEnd();
|
|
}
|
|
if (aProperty.EqualsLiteral("requestStart")) {
|
|
return Timing()->RequestStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("responseStart")) {
|
|
return Timing()->ResponseStart();
|
|
}
|
|
if (aProperty.EqualsLiteral("responseEnd")) {
|
|
return Timing()->ResponseEnd();
|
|
}
|
|
// Values from GetDOMTiming() are not.
|
|
DOMHighResTimeStamp retValue;
|
|
if (aProperty.EqualsLiteral("navigationStart")) {
|
|
// DOMHighResTimeStamp is in relation to navigationStart, so this will be
|
|
// zero.
|
|
retValue = GetDOMTiming()->GetNavigationStart();
|
|
} else if (aProperty.EqualsLiteral("unloadEventStart")) {
|
|
retValue = GetDOMTiming()->GetUnloadEventStart();
|
|
} else if (aProperty.EqualsLiteral("unloadEventEnd")) {
|
|
retValue = GetDOMTiming()->GetUnloadEventEnd();
|
|
} else if (aProperty.EqualsLiteral("domLoading")) {
|
|
retValue = GetDOMTiming()->GetDomLoading();
|
|
} else if (aProperty.EqualsLiteral("domInteractive")) {
|
|
retValue = GetDOMTiming()->GetDomInteractive();
|
|
} else if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
|
|
retValue = GetDOMTiming()->GetDomContentLoadedEventStart();
|
|
} else if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
|
|
retValue = GetDOMTiming()->GetDomContentLoadedEventEnd();
|
|
} else if (aProperty.EqualsLiteral("domComplete")) {
|
|
retValue = GetDOMTiming()->GetDomComplete();
|
|
} else if (aProperty.EqualsLiteral("loadEventStart")) {
|
|
retValue = GetDOMTiming()->GetLoadEventStart();
|
|
} else if (aProperty.EqualsLiteral("loadEventEnd")) {
|
|
retValue = GetDOMTiming()->GetLoadEventEnd();
|
|
} else {
|
|
MOZ_CRASH(
|
|
"IsPerformanceTimingAttribute and GetPerformanceTimingFromString are "
|
|
"out "
|
|
"of sync");
|
|
}
|
|
return nsRFPService::ReduceTimePrecisionAsMSecs(
|
|
retValue, GetRandomTimelineSeed(), /* aIsSystemPrinciapl */ false,
|
|
CrossOriginIsolated());
|
|
}
|
|
|
|
void PerformanceMainThread::InsertUserEntry(PerformanceEntry* aEntry) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsAutoCString uri;
|
|
double markCreationEpoch = 0;
|
|
|
|
if (StaticPrefs::dom_performance_enable_user_timing_logging() ||
|
|
StaticPrefs::dom_performance_enable_notify_performance_timing()) {
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
|
|
if (owner && owner->GetDocumentURI()) {
|
|
rv = owner->GetDocumentURI()->GetHost(uri);
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
// If we have no URI, just put in "none".
|
|
uri.AssignLiteral("none");
|
|
}
|
|
|
|
// PR_Now() returns a signed 64-bit integer. Since it represents a
|
|
// timestamp, only ~32-bits will represent the value which should safely fit
|
|
// into a double.
|
|
markCreationEpoch = static_cast<double>(PR_Now() / PR_USEC_PER_MSEC);
|
|
|
|
if (StaticPrefs::dom_performance_enable_user_timing_logging()) {
|
|
Performance::LogEntry(aEntry, uri);
|
|
}
|
|
}
|
|
|
|
if (StaticPrefs::dom_performance_enable_notify_performance_timing()) {
|
|
TimingNotification(aEntry, uri, markCreationEpoch);
|
|
}
|
|
|
|
Performance::InsertUserEntry(aEntry);
|
|
}
|
|
|
|
TimeStamp PerformanceMainThread::CreationTimeStamp() const {
|
|
return GetDOMTiming()->GetNavigationStartTimeStamp();
|
|
}
|
|
|
|
DOMHighResTimeStamp PerformanceMainThread::CreationTime() const {
|
|
return GetDOMTiming()->GetNavigationStart();
|
|
}
|
|
|
|
void PerformanceMainThread::CreateNavigationTimingEntry() {
|
|
MOZ_ASSERT(!mDocEntry, "mDocEntry should be null.");
|
|
|
|
if (!StaticPrefs::dom_enable_performance_navigation_timing() ||
|
|
StaticPrefs::privacy_resistFingerprinting()) {
|
|
return;
|
|
}
|
|
|
|
nsAutoString name;
|
|
GetURLSpecFromChannel(mChannel, name);
|
|
|
|
UniquePtr<PerformanceTimingData> timing(
|
|
new PerformanceTimingData(mChannel, nullptr, 0));
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
|
if (httpChannel) {
|
|
timing->SetPropertiesFromHttpChannel(httpChannel, mChannel);
|
|
}
|
|
|
|
mDocEntry = new PerformanceNavigationTiming(std::move(timing), this, name);
|
|
}
|
|
|
|
void PerformanceMainThread::UpdateNavigationTimingEntry() {
|
|
if (!mDocEntry) {
|
|
return;
|
|
}
|
|
|
|
// Let's update some values.
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
|
if (httpChannel) {
|
|
mDocEntry->UpdatePropertiesFromHttpChannel(httpChannel, mChannel);
|
|
}
|
|
}
|
|
|
|
void PerformanceMainThread::QueueNavigationTimingEntry() {
|
|
if (!mDocEntry) {
|
|
return;
|
|
}
|
|
|
|
UpdateNavigationTimingEntry();
|
|
|
|
QueueEntry(mDocEntry);
|
|
}
|
|
|
|
bool PerformanceMainThread::CrossOriginIsolated() const {
|
|
return mCrossOriginIsolated;
|
|
}
|
|
|
|
EventCounts* PerformanceMainThread::EventCounts() {
|
|
MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
|
|
return mEventCounts;
|
|
}
|
|
|
|
void PerformanceMainThread::GetEntries(
|
|
nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
|
|
// We return an empty list when 'privacy.resistFingerprinting' is on.
|
|
if (nsContentUtils::ShouldResistFingerprinting()) {
|
|
aRetval.Clear();
|
|
return;
|
|
}
|
|
|
|
aRetval = mResourceEntries.Clone();
|
|
aRetval.AppendElements(mUserEntries);
|
|
|
|
if (mDocEntry) {
|
|
aRetval.AppendElement(mDocEntry);
|
|
}
|
|
|
|
if (mFCPTiming) {
|
|
aRetval.AppendElement(mFCPTiming);
|
|
}
|
|
aRetval.Sort(PerformanceEntryComparator());
|
|
}
|
|
|
|
void PerformanceMainThread::GetEntriesByType(
|
|
const nsAString& aEntryType, nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
|
|
// We return an empty list when 'privacy.resistFingerprinting' is on.
|
|
if (nsContentUtils::ShouldResistFingerprinting()) {
|
|
aRetval.Clear();
|
|
return;
|
|
}
|
|
|
|
RefPtr<nsAtom> type = NS_Atomize(aEntryType);
|
|
if (type == nsGkAtoms::navigation) {
|
|
aRetval.Clear();
|
|
|
|
if (mDocEntry) {
|
|
aRetval.AppendElement(mDocEntry);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (type == nsGkAtoms::paint) {
|
|
if (mFCPTiming) {
|
|
aRetval.AppendElement(mFCPTiming);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (type == nsGkAtoms::firstInput && mFirstInputEvent) {
|
|
aRetval.AppendElement(mFirstInputEvent);
|
|
return;
|
|
}
|
|
|
|
Performance::GetEntriesByType(aEntryType, aRetval);
|
|
}
|
|
void PerformanceMainThread::GetEntriesByTypeForObserver(
|
|
const nsAString& aEntryType, nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
|
|
if (aEntryType.EqualsLiteral("event")) {
|
|
aRetval.AppendElements(mEventTimingEntries);
|
|
return;
|
|
}
|
|
return GetEntriesByType(aEntryType, aRetval);
|
|
}
|
|
|
|
void PerformanceMainThread::GetEntriesByName(
|
|
const nsAString& aName, const Optional<nsAString>& aEntryType,
|
|
nsTArray<RefPtr<PerformanceEntry>>& aRetval) {
|
|
// We return an empty list when 'privacy.resistFingerprinting' is on.
|
|
if (nsContentUtils::ShouldResistFingerprinting()) {
|
|
aRetval.Clear();
|
|
return;
|
|
}
|
|
|
|
Performance::GetEntriesByName(aName, aEntryType, aRetval);
|
|
|
|
if (mFCPTiming && mFCPTiming->GetName()->Equals(aName) &&
|
|
(!aEntryType.WasPassed() ||
|
|
mFCPTiming->GetEntryType()->Equals(aEntryType.Value()))) {
|
|
aRetval.AppendElement(mFCPTiming);
|
|
return;
|
|
}
|
|
|
|
// The navigation entry is the first one. If it exists and the name matches,
|
|
// let put it in front.
|
|
if (mDocEntry && mDocEntry->GetName()->Equals(aName)) {
|
|
aRetval.InsertElementAt(0, mDocEntry);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mozilla::PresShell* PerformanceMainThread::GetPresShell() {
|
|
nsIGlobalObject* ownerGlobal = GetOwnerGlobal();
|
|
if (!ownerGlobal) {
|
|
return nullptr;
|
|
}
|
|
if (Document* doc = ownerGlobal->AsInnerWindow()->GetExtantDoc()) {
|
|
return doc->GetPresShell();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void PerformanceMainThread::IncEventCount(const nsAtom* aType) {
|
|
MOZ_ASSERT(StaticPrefs::dom_enable_event_timing());
|
|
|
|
// This occurs when the pref was false when the performance
|
|
// object was first created, and became true later. It's
|
|
// okay to return early because eventCounts is not exposed.
|
|
if (!mEventCounts) {
|
|
return;
|
|
}
|
|
|
|
ErrorResult rv;
|
|
uint64_t count = EventCounts_Binding::MaplikeHelpers::Get(
|
|
mEventCounts, nsDependentAtomString(aType), rv);
|
|
MOZ_ASSERT(!rv.Failed());
|
|
EventCounts_Binding::MaplikeHelpers::Set(
|
|
mEventCounts, nsDependentAtomString(aType), ++count, rv);
|
|
MOZ_ASSERT(!rv.Failed());
|
|
}
|
|
|
|
size_t PerformanceMainThread::SizeOfEventEntries(
|
|
mozilla::MallocSizeOf aMallocSizeOf) const {
|
|
size_t eventEntries = 0;
|
|
for (const PerformanceEventTiming* entry : mEventTimingEntries) {
|
|
eventEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
return eventEntries;
|
|
}
|
|
} // namespace mozilla::dom
|