diff options
author | FranklinDM <mrmineshafter17@gmail.com> | 2023-04-05 22:22:34 +0800 |
---|---|---|
committer | FranklinDM <mrmineshafter17@gmail.com> | 2023-04-10 18:22:53 +0800 |
commit | d4138780ee267baf56250bb3a592419202553385 (patch) | |
tree | eff468633e02e8277efc289c8694e826d0cc663a /dom/performance | |
parent | 5fc711ba17cd86a7f168c159b551acdb598dbf1f (diff) | |
download | uxp-d4138780ee267baf56250bb3a592419202553385.tar.gz |
Issue #2053 - Part 2: Update PerformanceMeasure to User Timing L3
There are a few minor differences between this and Mozilla's implementation:
(a) The type error messages were not hardcoded to the Performance class and were moved to Errors.msg instead.
(b) PerformanceMeasureOptions is used directly and doesn't use the the Maybe<> container class. I haven't found the reason yet, but PerformanceMeasureOptions is disallowed from having a copy constructor and without that, it isn't possible to use it with Maybe<>... There doesn't seem to be any problem with using it directly, though.
(c) Resist fingerprinting-pref changes were skipped.
Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=1762482
Diffstat (limited to 'dom/performance')
-rwxr-xr-x | dom/performance/Performance.cpp | 207 | ||||
-rw-r--r-- | dom/performance/Performance.h | 39 | ||||
-rw-r--r-- | dom/performance/PerformanceMeasure.cpp | 45 | ||||
-rw-r--r-- | dom/performance/PerformanceMeasure.h | 12 |
4 files changed, 261 insertions, 42 deletions
diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp index 020dc0d89a..ebdddeb835 100755 --- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -20,6 +20,7 @@ #include "mozilla/dom/PerformanceNavigationBinding.h" #include "mozilla/dom/PerformanceObserverBinding.h" #include "mozilla/dom/PerformanceNavigationTiming.h" +#include "mozilla/dom/MessagePortBinding.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Preferences.h" #include "mozilla/TimerClamping.h" @@ -65,6 +66,12 @@ private: } // anonymous namespace +enum class Performance::ResolveTimestampAttribute { + Start, + End, + Duration, +}; + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Performance) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) @@ -257,8 +264,8 @@ Performance::ClearMarks(const Optional<nsAString>& aName) } DOMHighResTimeStamp -Performance::ResolveTimestampFromName(const nsAString& aName, - ErrorResult& aRv) +Performance::ConvertMarkToTimestampWithString(const nsAString& aName, + ErrorResult& aRv) { AutoTArray<RefPtr<PerformanceEntry>, 1> arr; DOMHighResTimeStamp ts; @@ -272,7 +279,7 @@ Performance::ResolveTimestampFromName(const nsAString& aName, } if (!IsPerformanceTimingAttribute(aName)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + aRv.ThrowTypeError<MSG_PMO_UNKNOWN_MARK_NAME>(aName); return 0; } @@ -285,50 +292,192 @@ Performance::ResolveTimestampFromName(const nsAString& aName, return ts - CreationTime(); } -void -Performance::Measure(const nsAString& aName, - const Optional<nsAString>& aStartMark, +DOMHighResTimeStamp +Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp( + const ResolveTimestampAttribute aAttribute, + const DOMHighResTimeStamp aTimestamp, + ErrorResult& aRv) +{ + if (aTimestamp < 0) { + nsAutoString attributeName; + switch (aAttribute) { + case ResolveTimestampAttribute::Start: + attributeName = NS_LITERAL_STRING("start"); + break; + case ResolveTimestampAttribute::End: + attributeName = NS_LITERAL_STRING("end"); + break; + case ResolveTimestampAttribute::Duration: + attributeName = NS_LITERAL_STRING("duration"); + break; + } + + aRv.ThrowTypeError<MSG_NO_NEGATIVE_ATTR>(attributeName); + } + return aTimestamp; +} + +DOMHighResTimeStamp +Performance::ConvertMarkToTimestamp( + const ResolveTimestampAttribute aAttribute, + const OwningStringOrDouble& aMarkNameOrTimestamp, + ErrorResult& aRv) +{ + if (aMarkNameOrTimestamp.IsString()) { + return ConvertMarkToTimestampWithString(aMarkNameOrTimestamp.GetAsString(), + aRv); + } + return ConvertMarkToTimestampWithDOMHighResTimeStamp( + aAttribute, aMarkNameOrTimestamp.GetAsDouble(), aRv); +} + +DOMHighResTimeStamp +Performance::ResolveEndTimeForMeasure( + const Optional<nsAString>& aEndMark, + const PerformanceMeasureOptions* aOptions, + ErrorResult& aRv) +{ + DOMHighResTimeStamp endTime; + if (aEndMark.WasPassed()) { + endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv); + } else if (aOptions != nullptr && aOptions->mEnd.WasPassed()) { + endTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::End, + aOptions->mEnd.Value(), aRv); + } else if (aOptions != nullptr && aOptions->mStart.WasPassed() && + aOptions->mDuration.WasPassed()) { + const DOMHighResTimeStamp start = ConvertMarkToTimestamp( + ResolveTimestampAttribute::Start, aOptions->mStart.Value(), aRv); + if (aRv.Failed()) { + return 0; + } + const DOMHighResTimeStamp duration = + ConvertMarkToTimestampWithDOMHighResTimeStamp( + ResolveTimestampAttribute::Duration, + aOptions->mDuration.Value(), + aRv); + if (aRv.Failed()) { + return 0; + } + + endTime = start + duration; + } else { + endTime = Now(); + } + return endTime; +} + +DOMHighResTimeStamp +Performance::ResolveStartTimeForMeasure( + const nsAString* aStartMark, + const PerformanceMeasureOptions* aOptions, + ErrorResult& aRv) +{ + DOMHighResTimeStamp startTime; + if (aOptions != nullptr && aOptions->mStart.WasPassed()) { + startTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::Start, + aOptions->mStart.Value(), + aRv); + } else if (aOptions != nullptr && aOptions->mDuration.WasPassed() && + aOptions->mEnd.WasPassed()) { + const DOMHighResTimeStamp duration = + ConvertMarkToTimestampWithDOMHighResTimeStamp( + ResolveTimestampAttribute::Duration, + aOptions->mDuration.Value(), + aRv); + if (aRv.Failed()) { + return 0; + } + + const DOMHighResTimeStamp end = ConvertMarkToTimestamp( + ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); + if (aRv.Failed()) { + return 0; + } + + startTime = end - duration; + } else if (aStartMark != nullptr) { + startTime = ConvertMarkToTimestampWithString(*aStartMark, aRv); + } else { + startTime = 0; + } + + return startTime; +} + +already_AddRefed<PerformanceMeasure> +Performance::Measure(JSContext* aCx, + const nsAString& aName, + const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, const Optional<nsAString>& aEndMark, ErrorResult& aRv) { // Don't add the entry if the buffer is full. XXX should be removed by bug // 1159003. if (mUserEntries.Length() >= mResourceTimingBufferSize) { - return; + return nullptr; } - DOMHighResTimeStamp startTime; - DOMHighResTimeStamp endTime; - - if (IsPerformanceTimingAttribute(aName)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return; + const PerformanceMeasureOptions* options = nullptr; + if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) { + options = &aStartOrMeasureOptions.GetAsPerformanceMeasureOptions(); } - if (aStartMark.WasPassed()) { - startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); - if (NS_WARN_IF(aRv.Failed())) { - return; + const bool isOptionsNotEmpty = + (options != nullptr) && + (!options->mDetail.isUndefined() || options->mStart.WasPassed() || + options->mEnd.WasPassed() || options->mDuration.WasPassed()); + if (isOptionsNotEmpty) { + if (aEndMark.WasPassed()) { + aRv.ThrowTypeError<MSG_PMO_NO_SEPARATE_ENDMARK>(); + return nullptr; + } + + if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) { + aRv.ThrowTypeError<MSG_PMO_MISSING_STARTENDMARK>(); + return nullptr; + } + + if (options->mStart.WasPassed() && options->mDuration.WasPassed() && + options->mEnd.WasPassed()) { + aRv.ThrowTypeError<MSG_PMO_INVALID_MEMBERS>(); + return nullptr; } - } else { - // Navigation start is used in this case, but since DOMHighResTimeStamp is - // in relation to navigation start, this will be zero if a name is not - // passed. - startTime = 0; } - if (aEndMark.WasPassed()) { - endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); - if (NS_WARN_IF(aRv.Failed())) { - return; + const DOMHighResTimeStamp endTime = + ResolveEndTimeForMeasure(aEndMark, options, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + const nsAString* startMark = nullptr; + if (aStartOrMeasureOptions.IsString()) { + startMark = &aStartOrMeasureOptions.GetAsString(); + } + const DOMHighResTimeStamp startTime = + ResolveStartTimeForMeasure(startMark, options, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + JS::Rooted<JS::Value> detail(aCx); + if (options != nullptr && !options->mDetail.isNullOrUndefined()) { + StructuredSerializeOptions serializeOptions; + JS::Rooted<JS::Value> valueToClone(aCx, options->mDetail); + nsContentUtils::StructuredClone(aCx, GetParentObject(), valueToClone, + serializeOptions, &detail, aRv); + if (aRv.Failed()) { + return nullptr; } } else { - endTime = Now(); + detail.setNull(); } - RefPtr<PerformanceMeasure> performanceMeasure = - new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime); + RefPtr<PerformanceMeasure> performanceMeasure = new PerformanceMeasure( + GetAsISupports(), aName, startTime, endTime, detail); InsertUserEntry(performanceMeasure); + + return performanceMeasure.forget(); } void diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h index 2574fcdd3e..772e88c6da 100644 --- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -20,11 +20,16 @@ class ErrorResult; namespace dom { +class OwningStringOrDouble; +class StringOrPerformanceMeasureOptions; class PerformanceEntry; +struct PerformanceMeasureOptions; +class PerformanceMeasure; class PerformanceNavigation; class PerformanceObserver; class PerformanceService; class PerformanceTiming; +struct StructuredSerializeOptions; namespace workers { class WorkerPrivate; @@ -75,10 +80,10 @@ public: void ClearMarks(const Optional<nsAString>& aName); - void Measure(const nsAString& aName, - const Optional<nsAString>& aStartMark, - const Optional<nsAString>& aEndMark, - ErrorResult& aRv); + already_AddRefed<PerformanceMeasure> Measure( + JSContext* aCx, const nsAString& aName, + const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, + const Optional<nsAString>& aEndMark, ErrorResult& aRv); void ClearMeasures(const Optional<nsAString>& aName); @@ -116,9 +121,6 @@ protected: void ClearUserEntries(const Optional<nsAString>& aEntryName, const nsAString& aEntryType); - DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, - ErrorResult& aRv); - virtual nsISupports* GetAsISupports() = 0; virtual void DispatchBufferFullEvent() = 0; @@ -163,6 +165,29 @@ protected: bool mPendingNotificationObserversTask; RefPtr<PerformanceService> mPerformanceService; + +private: + // The attributes of a PerformanceMeasureOptions that we call + // ResolveTimestamp* on. + enum class ResolveTimestampAttribute; + + DOMHighResTimeStamp ConvertMarkToTimestampWithString(const nsAString& aName, + ErrorResult& aRv); + DOMHighResTimeStamp ConvertMarkToTimestampWithDOMHighResTimeStamp( + const ResolveTimestampAttribute aAttribute, const double aTimestamp, + ErrorResult& aRv); + DOMHighResTimeStamp ConvertMarkToTimestamp( + const ResolveTimestampAttribute aAttribute, + const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv); + + DOMHighResTimeStamp ResolveEndTimeForMeasure( + const Optional<nsAString>& aEndMark, + const PerformanceMeasureOptions* aOptions, + ErrorResult& aRv); + DOMHighResTimeStamp ResolveStartTimeForMeasure( + const nsAString* aStartMark, + const PerformanceMeasureOptions* aOptions, + ErrorResult& aRv); }; } // namespace dom diff --git a/dom/performance/PerformanceMeasure.cpp b/dom/performance/PerformanceMeasure.cpp index 2e429e6814..3c6e73230f 100644 --- a/dom/performance/PerformanceMeasure.cpp +++ b/dom/performance/PerformanceMeasure.cpp @@ -12,10 +12,12 @@ using namespace mozilla::dom; PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, - DOMHighResTimeStamp aEndTime) -: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure")), - mStartTime(aStartTime), - mDuration(aEndTime - aStartTime) + DOMHighResTimeStamp aEndTime, + const JS::Handle<JS::Value>& aDetail) + : PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure")) + , mStartTime(aStartTime) + , mDuration(aEndTime - aStartTime) + , mDetail(aDetail) { // mParent is null in workers. MOZ_ASSERT(mParent || !NS_IsMainThread()); @@ -23,10 +25,43 @@ PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, PerformanceMeasure::~PerformanceMeasure() { + mozilla::DropJSObjects(this); } +NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMeasure) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMeasure, + PerformanceEntry) + tmp->mDetail.setUndefined(); + mozilla::DropJSObjects(tmp); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMeasure, + PerformanceEntry) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMeasure, + PerformanceEntry) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDetail) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMeasure) +NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry) +NS_IMPL_ADDREF_INHERITED(PerformanceMeasure, PerformanceEntry) +NS_IMPL_RELEASE_INHERITED(PerformanceMeasure, PerformanceEntry) + JSObject* -PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +PerformanceMeasure::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { return PerformanceMeasureBinding::Wrap(aCx, this, aGivenProto); } + +void +PerformanceMeasure::GetDetail(JSContext* aCx, + JS::MutableHandle<JS::Value> aRv) +{ + // Return a copy so that this method always returns the value it is set to + // (i.e. it'll return the same value even if the caller assigns to it). Note + // that if detail is an object, its contents can be mutated and this is + // expected. + aRv.set(mDetail); +} diff --git a/dom/performance/PerformanceMeasure.h b/dom/performance/PerformanceMeasure.h index 5e70420d96..83d2ba3406 100644 --- a/dom/performance/PerformanceMeasure.h +++ b/dom/performance/PerformanceMeasure.h @@ -15,10 +15,15 @@ namespace dom { class PerformanceMeasure final : public PerformanceEntry { public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMeasure, + PerformanceEntry); + PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, - DOMHighResTimeStamp aEndTime); + DOMHighResTimeStamp aEndTime, + const JS::Handle<JS::Value>& aDetail); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; @@ -32,10 +37,15 @@ public: return mDuration; } + void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRv); + protected: virtual ~PerformanceMeasure(); DOMHighResTimeStamp mStartTime; DOMHighResTimeStamp mDuration; + +private: + JS::Heap<JS::Value> mDetail; }; } // namespace dom |