Bug 1667836 - Implement PerformanceEventTiming Interface r=smaug

Spec: https://wicg.github.io/event-timing/#sec-performance-event-timing

Differential Revision: https://phabricator.services.mozilla.com/D102035
This commit is contained in:
Sean Feng 2021-02-09 18:54:47 +00:00
parent 3ae0be12e8
commit 228f53a393
19 changed files with 599 additions and 19 deletions

View File

@ -6575,6 +6575,8 @@ void nsGlobalWindowInner::AddSizeOfIncludingThis(
mPerformance->SizeOfUserEntries(aWindowSizes.mState.mMallocSizeOf);
aWindowSizes.mDOMPerformanceResourceEntries =
mPerformance->SizeOfResourceEntries(aWindowSizes.mState.mMallocSizeOf);
aWindowSizes.mDOMPerformanceEventEntries =
mPerformance->SizeOfEventEntries(aWindowSizes.mState.mMallocSizeOf);
}
}

View File

@ -140,6 +140,7 @@ class nsWindowSizes {
MACRO(DOM, mDOMCommentNodesSize) \
MACRO(DOM, mDOMEventTargetsSize) \
MACRO(DOM, mDOMMediaQueryLists) \
MACRO(DOM, mDOMPerformanceEventEntries) \
MACRO(DOM, mDOMPerformanceUserEntries) \
MACRO(DOM, mDOMPerformanceResourceEntries) \
MACRO(DOM, mDOMResizeObserverControllerSize) \

View File

@ -40,6 +40,7 @@
#include "mozilla/dom/MutationEvent.h"
#include "mozilla/dom/NotifyPaintEvent.h"
#include "mozilla/dom/PageTransitionEvent.h"
#include "mozilla/dom/PerformanceEventTiming.h"
#include "mozilla/dom/PointerEvent.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/ScrollAreaEvent.h"
@ -762,6 +763,14 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
RefPtr<PerformanceEventTiming> eventTimingEntry;
// Similar to PerformancePaintTiming, we don't need to
// expose them for printing documents
if (aPresContext && !aPresContext->IsPrintingOrPrintPreview()) {
eventTimingEntry =
PerformanceEventTiming::TryGenerateEventTiming(target, aEvent);
}
bool retargeted = false;
if (aEvent->mFlags.mRetargetToNonNativeAnonymous) {
@ -1118,6 +1127,9 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
aEvent->mFlags.mIsBeingDispatched = false;
aEvent->mFlags.mDispatchedAtLeastOnce = true;
if (eventTimingEntry) {
eventTimingEntry->FinalizeEventTiming(aEvent->mTarget);
}
// https://dom.spec.whatwg.org/#concept-event-dispatch
// step 10. If clearTargets, then:
// 1. Set event's target to null.

View File

@ -656,25 +656,23 @@ void Performance::RunNotificationObserversTask() {
}
void Performance::QueueEntry(PerformanceEntry* aEntry) {
if (mObservers.IsEmpty()) {
return;
}
nsTObserverArray<PerformanceObserver*> interestedObservers;
const auto [begin, end] = mObservers.NonObservingRange();
std::copy_if(begin, end, MakeBackInserter(interestedObservers),
[aEntry](PerformanceObserver* observer) {
return observer->ObservesTypeOfEntry(aEntry);
});
if (interestedObservers.IsEmpty()) {
return;
if (!mObservers.IsEmpty()) {
const auto [begin, end] = mObservers.NonObservingRange();
std::copy_if(begin, end, MakeBackInserter(interestedObservers),
[aEntry](PerformanceObserver* observer) {
return observer->ObservesTypeOfEntry(aEntry);
});
}
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(interestedObservers, QueueEntry,
(aEntry));
QueueNotificationObserversTask();
aEntry->BufferEntryIfNeeded();
if (!interestedObservers.IsEmpty()) {
QueueNotificationObserversTask();
}
}
void Performance::MemoryPressure() { mUserEntries.Clear(); }

View File

@ -28,6 +28,7 @@ class PerformanceObserver;
class PerformanceService;
class PerformanceStorage;
class PerformanceTiming;
class PerformanceEventTiming;
class WorkerPrivate;
// Base class for main-thread and worker Performance API
@ -110,15 +111,25 @@ class Performance : public DOMEventTargetHelper {
size_t SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
size_t SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
virtual size_t SizeOfEventEntries(mozilla::MallocSizeOf aMallocSizeOf) const {
return 0;
}
void InsertResourceEntry(PerformanceEntry* aEntry);
virtual void InsertEventTimingEntry(PerformanceEventTiming* aEntry) = 0;
virtual void BufferEventTimingEntryIfNeeded(
PerformanceEventTiming* aEntry) = 0;
virtual void QueueNavigationTimingEntry() = 0;
virtual void UpdateNavigationTimingEntry() = 0;
virtual bool CrossOriginIsolated() const = 0;
virtual void DispatchPendingEventTimingEntries() = 0;
void QueueNotificationObserversTask();
protected:

View File

@ -64,6 +64,8 @@ class PerformanceEntry : public nsISupports, public nsWrapperCache {
virtual bool ShouldAddEntryToObserverBuffer(
PerformanceObserverInit& aOption) const;
virtual void BufferEntryIfNeeded() {}
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
protected:

View File

@ -0,0 +1,226 @@
/* -*- 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 "PerformanceEventTiming.h"
#include "PerformanceMainThread.h"
#include "mozilla/dom/PerformanceEventTimingBinding.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/Event.h"
#include "nsIDocShell.h"
#include <algorithm>
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceEventTiming, PerformanceEntry,
mPerformance, mTarget)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceEventTiming)
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
NS_IMPL_ADDREF_INHERITED(PerformanceEventTiming, PerformanceEntry)
NS_IMPL_RELEASE_INHERITED(PerformanceEventTiming, PerformanceEntry)
PerformanceEventTiming::PerformanceEventTiming(Performance* aPerformance,
const nsAString& aName,
const TimeStamp& aStartTime,
bool aIsCacelable,
EventMessage aMessage)
: PerformanceEntry(aPerformance->GetParentObject(), aName, u"event"_ns),
mPerformance(aPerformance),
mProcessingStart(aPerformance->NowUnclamped()),
mProcessingEnd(0),
mStartTime(
aPerformance->GetDOMTiming()->TimeStampToDOMHighRes(aStartTime)),
mDuration(0),
mCancelable(aIsCacelable),
mMessage(aMessage) {}
PerformanceEventTiming::PerformanceEventTiming(
const PerformanceEventTiming& aEventTimingEntry)
: PerformanceEntry(aEventTimingEntry.mPerformance->GetParentObject(),
nsDependentAtomString(aEventTimingEntry.GetName()),
nsDependentAtomString(aEventTimingEntry.GetEntryType())),
mPerformance(aEventTimingEntry.mPerformance),
mProcessingStart(aEventTimingEntry.mProcessingStart),
mProcessingEnd(aEventTimingEntry.mProcessingEnd),
mTarget(aEventTimingEntry.mTarget),
mStartTime(aEventTimingEntry.mStartTime),
mDuration(aEventTimingEntry.mDuration),
mCancelable(aEventTimingEntry.mCancelable),
mMessage(aEventTimingEntry.mMessage) {}
JSObject* PerformanceEventTiming::WrapObject(
JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
return PerformanceEventTiming_Binding::Wrap(cx, this, aGivenProto);
}
already_AddRefed<PerformanceEventTiming>
PerformanceEventTiming::TryGenerateEventTiming(const EventTarget* aTarget,
const WidgetEvent* aEvent) {
MOZ_ASSERT(NS_IsMainThread());
if (!StaticPrefs::dom_enable_event_timing() ||
aEvent->mFlags.mOnlyChromeDispatch) {
return nullptr;
}
if (!aEvent->IsTrusted()) {
return nullptr;
}
switch (aEvent->mMessage) {
case eMouseAuxClick:
case eMouseClick:
case eContextMenu:
case eMouseDoubleClick:
case eMouseDown:
case eMouseEnter:
case eMouseLeave:
case eMouseOut:
case eMouseOver:
case eMouseUp:
case ePointerOver:
case ePointerEnter:
case ePointerDown:
case ePointerUp:
case ePointerCancel:
case ePointerOut:
case ePointerLeave:
case ePointerGotCapture:
case ePointerLostCapture:
case eTouchStart:
case eTouchEnd:
case eTouchCancel:
case eKeyDown:
case eKeyPress:
case eKeyUp:
case eEditorBeforeInput:
case eEditorInput:
case eCompositionStart:
case eCompositionUpdate:
case eCompositionEnd:
case eDragStart:
case eDragEnd:
case eDragEnter:
case eDragLeave:
case eDragOver:
case eDrop:
break;
default:
return nullptr;
}
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
do_QueryInterface(aTarget->GetOwnerGlobal());
if (!innerWindow) {
return nullptr;
}
if (Performance* performance = innerWindow->GetPerformance()) {
const char* eventName = Event::GetEventName(aEvent->mMessage);
MOZ_ASSERT(eventName,
"User defined events shouldn't be considered as event timing");
return RefPtr<PerformanceEventTiming>(
new PerformanceEventTiming(
performance, NS_ConvertUTF8toUTF16(eventName),
aEvent->mTimeStamp, aEvent->mFlags.mCancelable,
aEvent->mMessage))
.forget();
}
return nullptr;
}
bool PerformanceEventTiming::ShouldAddEntryToBuffer(double aDuration) const {
if (GetEntryType() == nsGkAtoms::firstInput) {
return true;
}
MOZ_ASSERT(GetEntryType() == nsGkAtoms::event);
return Duration() >= aDuration;
}
bool PerformanceEventTiming::ShouldAddEntryToObserverBuffer(
PerformanceObserverInit& aOption) const {
if (!PerformanceEntry::ShouldAddEntryToObserverBuffer(aOption)) {
return false;
}
const double minDuration =
aOption.mDurationThreshold.WasPassed()
? std::max(aOption.mDurationThreshold.Value(),
PerformanceMainThread::kDefaultEventTimingMinDuration)
: PerformanceMainThread::kDefaultEventTimingDurationThreshold;
return ShouldAddEntryToBuffer(minDuration);
}
void PerformanceEventTiming::BufferEntryIfNeeded() {
if (ShouldAddEntryToBuffer(
PerformanceMainThread::kDefaultEventTimingDurationThreshold)) {
if (GetEntryType() != nsGkAtoms::firstInput) {
MOZ_ASSERT(GetEntryType() == nsGkAtoms::event);
mPerformance->BufferEventTimingEntryIfNeeded(this);
}
}
}
nsINode* PerformanceEventTiming::GetTarget() const {
if (!mTarget) {
return nullptr;
}
nsCOMPtr<nsINode> target = do_QueryReferent(mTarget);
if (!target || !target->IsInUncomposedDoc()) {
return nullptr;
}
return target;
}
void PerformanceEventTiming::FinalizeEventTiming(EventTarget* aTarget) {
if (!aTarget) {
return;
}
nsCOMPtr<nsPIDOMWindowInner> global =
do_QueryInterface(aTarget->GetOwnerGlobal());
if (!global) {
return;
}
mProcessingEnd = mPerformance->NowUnclamped();
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
if (!node || node->ChromeOnlyAccess()) {
return;
}
mTarget = do_GetWeakReference(GetAnElement(node, global->GetExtantDoc()));
mPerformance->InsertEventTimingEntry(this);
}
nsINode* PerformanceEventTiming::GetAnElement(nsINode* aTarget,
const Document* aDocument) {
if (!aTarget->IsInUncomposedDoc()) {
return nullptr;
}
if (!aDocument) {
nsCOMPtr<nsPIDOMWindowInner> inner =
do_QueryInterface(mPerformance->GetParentObject());
MOZ_ASSERT(inner);
aDocument = inner->GetExtantDoc();
}
MOZ_ASSERT(aDocument);
if (aTarget->GetComposedDoc() != aDocument ||
!aDocument->IsCurrentActiveDocument()) {
return nullptr;
}
return aTarget;
}
} // namespace mozilla::dom

View File

@ -0,0 +1,134 @@
/* -*- 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/. */
#ifndef mozilla_dom_PerformanceEventTiming_h___
#define mozilla_dom_PerformanceEventTiming_h___
#include "mozilla/dom/PerformanceEntry.h"
#include "mozilla/EventForwards.h"
#include "nsRFPService.h"
#include "Performance.h"
#include "nsIWeakReferenceUtils.h"
#include "nsINode.h"
namespace mozilla {
class WidgetEvent;
namespace dom {
class PerformanceEventTiming final
: public PerformanceEntry,
public LinkedListElement<RefPtr<PerformanceEventTiming>> {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PerformanceEventTiming,
PerformanceEntry)
static already_AddRefed<PerformanceEventTiming> TryGenerateEventTiming(
const EventTarget* aTarget, const WidgetEvent* aEvent);
already_AddRefed<PerformanceEventTiming> Clone() {
RefPtr<PerformanceEventTiming> eventTiming =
new PerformanceEventTiming(*this);
return eventTiming.forget();
}
JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) override;
DOMHighResTimeStamp ProcessingStart() const {
if (mCachedProcessingStart.isNothing()) {
mCachedProcessingStart.emplace(nsRFPService::ReduceTimePrecisionAsMSecs(
mProcessingStart, mPerformance->GetRandomTimelineSeed(),
mPerformance->IsSystemPrincipal(),
mPerformance->CrossOriginIsolated()));
}
return mCachedProcessingStart.value();
}
DOMHighResTimeStamp ProcessingEnd() const {
if (mCachedProcessingEnd.isNothing()) {
mCachedProcessingEnd.emplace(nsRFPService::ReduceTimePrecisionAsMSecs(
mProcessingEnd, mPerformance->GetRandomTimelineSeed(),
mPerformance->IsSystemPrincipal(),
mPerformance->CrossOriginIsolated()));
}
return mCachedProcessingEnd.value();
}
bool Cancelable() const { return mCancelable; }
nsINode* GetTarget() const;
void SetDuration(const DOMHighResTimeStamp aDuration) {
mDuration = aDuration;
}
DOMHighResTimeStamp Duration() const override {
if (mCachedDuration.isNothing()) {
mCachedDuration.emplace(nsRFPService::ReduceTimePrecisionAsMSecs(
mDuration, mPerformance->GetRandomTimelineSeed(),
mPerformance->IsSystemPrincipal(),
mPerformance->CrossOriginIsolated()));
}
return mCachedDuration.value();
}
DOMHighResTimeStamp StartTime() const override {
if (mCachedStartTime.isNothing()) {
mCachedStartTime.emplace(nsRFPService::ReduceTimePrecisionAsMSecs(
mStartTime, mPerformance->GetRandomTimelineSeed(),
mPerformance->IsSystemPrincipal(),
mPerformance->CrossOriginIsolated()));
}
return mCachedStartTime.value();
}
bool ShouldAddEntryToBuffer(double aDuration) const;
bool ShouldAddEntryToObserverBuffer(PerformanceObserverInit&) const override;
void BufferEntryIfNeeded() override;
void FinalizeEventTiming(EventTarget* aTarget);
EventMessage GetMessage() const { return mMessage; }
private:
PerformanceEventTiming(Performance* aPerformance, const nsAString& aName,
const TimeStamp& aStartTime, bool aIsCacelable,
EventMessage aMessage);
PerformanceEventTiming(const PerformanceEventTiming& aEventTimingEntry);
~PerformanceEventTiming() = default;
// Perform the get an element algorithm
nsINode* GetAnElement(nsINode* aTarget, const Document* aDocument);
RefPtr<Performance> mPerformance;
DOMHighResTimeStamp mProcessingStart;
mutable Maybe<DOMHighResTimeStamp> mCachedProcessingStart;
DOMHighResTimeStamp mProcessingEnd;
mutable Maybe<DOMHighResTimeStamp> mCachedProcessingEnd;
nsWeakPtr mTarget;
DOMHighResTimeStamp mStartTime;
mutable Maybe<DOMHighResTimeStamp> mCachedStartTime;
DOMHighResTimeStamp mDuration;
mutable Maybe<DOMHighResTimeStamp> mCachedDuration;
bool mCancelable;
EventMessage mMessage;
};
} // namespace dom
} // namespace mozilla
#endif

View File

@ -10,14 +10,18 @@
#include "js/GCAPI.h"
#include "jsapi.h"
#include "mozilla/HoldDropJSObjects.h"
#include "PerformanceEventTiming.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Event.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 {
@ -52,14 +56,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry, mFCPTiming)
NS_IMPL_CYCLE_COLLECTION_UNLINK(
mTiming, mNavigation, mDocEntry, mFCPTiming, mEventTimingEntries,
mFirstInputEvent, mPendingPointerDown, mPendingEventTimingEntries)
tmp->mMozMemory = nullptr;
mozilla::DropJSObjects(this);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming, mNavigation, mDocEntry, mFCPTiming)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
mTiming, mNavigation, mDocEntry, mFCPTiming, mEventTimingEntries,
mFirstInputEvent, mPendingPointerDown, mPendingEventTimingEntries)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
@ -114,8 +122,9 @@ void PerformanceMainThread::GetMozMemory(JSContext* aCx,
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.
// 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());
}
@ -184,6 +193,90 @@ void PerformanceMainThread::SetFCPTimingEntry(PerformancePaintTiming* 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 =
presContext->RegisterOneShotPostRefreshObserver(
new OneShotPostRefreshObserver(
presShell, [performance = RefPtr<PerformanceMainThread>(this)](
PresShell*, OneShotPostRefreshObserver*) {
performance->DispatchPendingEventTimingEntries();
}));
}
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->StartTime());
if (entry->Duration() >= 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;
}
}
}
mHasQueuedRefreshdriverObserver = false;
}
// To be removed once bug 1124165 lands
bool PerformanceMainThread::IsPerformanceTimingAttribute(
const nsAString& aName) {
@ -430,6 +523,11 @@ void PerformanceMainThread::GetEntriesByType(
}
}
if (type == nsGkAtoms::firstInput && mFirstInputEvent) {
aRetval.AppendElement(mFirstInputEvent);
return;
}
Performance::GetEntriesByType(aEntryType, aRetval);
}
@ -459,4 +557,23 @@ void PerformanceMainThread::GetEntriesByName(
}
}
mozilla::PresShell* PerformanceMainThread::GetPresShell() {
nsIGlobalObject* ownerGlobal = GetOwnerGlobal();
if (!ownerGlobal) {
return nullptr;
}
if (Document* doc = ownerGlobal->AsInnerWindow()->GetExtantDoc()) {
return doc->GetPresShell();
}
return nullptr;
}
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

View File

@ -14,6 +14,7 @@ namespace mozilla {
namespace dom {
class PerformanceNavigationTiming;
class PerformanceEventTiming;
class PerformanceMainThread final : public Performance,
public PerformanceStorage {
@ -46,6 +47,10 @@ class PerformanceMainThread final : public Performance,
const nsAString& aEntryName);
virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) override;
void InsertEventTimingEntry(PerformanceEventTiming*) override;
void BufferEventTimingEntryIfNeeded(PerformanceEventTiming*) override;
void DispatchPendingEventTimingEntries() override;
TimeStamp CreationTimeStamp() const override;
DOMHighResTimeStamp CreationTime() const override;
@ -66,6 +71,8 @@ class PerformanceMainThread final : public Performance,
// The GetEntries* methods need to be overriden in order to add the
// the document entry of type navigation.
virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
// Return entries which qualify availableFromTimeline boolean check
virtual void GetEntriesByType(
const nsAString& aEntryType,
nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
@ -78,6 +85,12 @@ class PerformanceMainThread final : public Performance,
bool CrossOriginIsolated() const override;
size_t SizeOfEventEntries(mozilla::MallocSizeOf aMallocSizeOf) const override;
static constexpr uint32_t kDefaultEventTimingBufferSize = 150;
static constexpr uint32_t kDefaultEventTimingDurationThreshold = 104;
static constexpr double kDefaultEventTimingMinDuration = 16.0;
protected:
~PerformanceMainThread();
@ -101,6 +114,21 @@ class PerformanceMainThread final : public Performance,
JS::Heap<JSObject*> mMozMemory;
const bool mCrossOriginIsolated;
AutoTArray<RefPtr<PerformanceEventTiming>, kDefaultEventTimingBufferSize>
mEventTimingEntries;
AutoCleanLinkedList<RefPtr<PerformanceEventTiming>>
mPendingEventTimingEntries;
bool mHasDispatchedInputEvent = false;
RefPtr<PerformanceEventTiming> mFirstInputEvent;
RefPtr<PerformanceEventTiming> mPendingPointerDown;
private:
bool mHasQueuedRefreshdriverObserver = false;
PresShell* GetPresShell();
};
} // namespace dom

View File

@ -132,8 +132,9 @@ void PerformanceObserver::QueueEntry(PerformanceEntry* aEntry) {
* Keep this list in alphabetical order.
* https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute
*/
static const char16_t* const sValidTypeNames[5] = {
u"mark", u"measure", u"navigation", u"paint", u"resource",
static const char16_t* const sValidTypeNames[7] = {
u"event", u"first-input", u"mark", u"measure",
u"navigation", u"paint", u"resource",
};
void PerformanceObserver::ReportUnsupportedTypesErrorToConsole(

View File

@ -66,6 +66,18 @@ class PerformanceWorker final : public Performance {
MOZ_CRASH("This should not be called on workers.");
}
void InsertEventTimingEntry(PerformanceEventTiming*) override {
MOZ_CRASH("This should not be called on workers.");
}
void BufferEventTimingEntryIfNeeded(PerformanceEventTiming*) override {
MOZ_CRASH("This should not be called on workers.");
}
void DispatchPendingEventTimingEntries() override {
MOZ_CRASH("This should not be called on workders.");
}
bool CrossOriginIsolated() const override;
protected:

View File

@ -10,6 +10,7 @@ with Files("**"):
EXPORTS.mozilla.dom += [
"Performance.h",
"PerformanceEntry.h",
"PerformanceEventTiming.h",
"PerformanceMainThread.h",
"PerformanceMark.h",
"PerformanceMeasure.h",
@ -29,6 +30,7 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
"Performance.cpp",
"PerformanceEntry.cpp",
"PerformanceEventTiming.cpp",
"PerformanceMainThread.cpp",
"PerformanceMark.cpp",
"PerformanceMeasure.cpp",

View File

@ -843,6 +843,8 @@ var interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PerformanceEntry", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PerformanceEventTiming", insecureContext: true, nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PerformanceMark", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PerformanceMeasure", insecureContext: true },

View File

@ -0,0 +1,22 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/navigation-timing/#the-performancenavigation-interface
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
[Pref="dom.enable_event_timing",
Exposed=Window]
interface PerformanceEventTiming : PerformanceEntry {
readonly attribute DOMHighResTimeStamp processingStart;
readonly attribute DOMHighResTimeStamp processingEnd;
readonly attribute boolean cancelable;
readonly attribute Node? target;
[Default] object toJSON();
};

View File

@ -11,6 +11,8 @@ dictionary PerformanceObserverInit {
sequence<DOMString> entryTypes;
DOMString type;
boolean buffered;
[Pref="dom.enable_event_timing"]
DOMHighResTimeStamp durationThreshold;
};
callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries,

View File

@ -746,6 +746,7 @@ WEBIDL_FILES = [
"PaymentResponse.webidl",
"Performance.webidl",
"PerformanceEntry.webidl",
"PerformanceEventTiming.webidl",
"PerformanceMark.webidl",
"PerformanceMeasure.webidl",
"PerformanceNavigation.webidl",

View File

@ -1725,6 +1725,12 @@
value: true
mirror: always
# Whether event timing will be gathered and returned by performance observer*
- name: dom.enable_event_timing
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Whether performance.GetEntries* will contain an entry for the active document
- name: dom.enable_performance_navigation_timing
type: bool

View File

@ -435,6 +435,7 @@ STATIC_ATOMS = [
Atom("figcaption", "figcaption"),
Atom("figure", "figure"),
Atom("findbar", "findbar"),
Atom("firstInput", "first-input"),
Atom("fixed", "fixed"),
Atom("flags", "flags"),
Atom("flex", "flex"),