mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00
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:
parent
3ae0be12e8
commit
228f53a393
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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.
|
||||
|
@ -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(); }
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
226
dom/performance/PerformanceEventTiming.cpp
Normal file
226
dom/performance/PerformanceEventTiming.cpp
Normal 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
|
134
dom/performance/PerformanceEventTiming.h
Normal file
134
dom/performance/PerformanceEventTiming.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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 },
|
||||
|
22
dom/webidl/PerformanceEventTiming.webidl
Normal file
22
dom/webidl/PerformanceEventTiming.webidl
Normal 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();
|
||||
};
|
@ -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,
|
||||
|
@ -746,6 +746,7 @@ WEBIDL_FILES = [
|
||||
"PaymentResponse.webidl",
|
||||
"Performance.webidl",
|
||||
"PerformanceEntry.webidl",
|
||||
"PerformanceEventTiming.webidl",
|
||||
"PerformanceMark.webidl",
|
||||
"PerformanceMeasure.webidl",
|
||||
"PerformanceNavigation.webidl",
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user