Loading dom/performance/Performance.cpp +184 −30 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "PerformanceWorker.h" #include "mozilla/BasePrincipal.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/PerformanceBinding.h" #include "mozilla/dom/PerformanceEntryEvent.h" #include "mozilla/dom/PerformanceNavigationBinding.h" Loading @@ -33,6 +34,12 @@ namespace mozilla::dom { enum class Performance::ResolveTimestampAttribute { Start, End, Duration, }; NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) Loading Loading @@ -387,7 +394,7 @@ void Performance::ClearMarks(const Optional<nsAString>& aName) { ClearUserEntries(aName, u"mark"_ns); } DOMHighResTimeStamp Performance::ResolveTimestampFromName( DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString( const nsAString& aName, ErrorResult& aRv) { AutoTArray<RefPtr<PerformanceEntry>, 1> arr; Optional<nsAString> typeParam; Loading @@ -400,7 +407,9 @@ DOMHighResTimeStamp Performance::ResolveTimestampFromName( } if (!IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); nsPrintfCString errorMsg("Given mark name, %s, is unknown", NS_ConvertUTF16toUTF8(aName).get()); aRv.ThrowSyntaxError(errorMsg); return 0; } Loading @@ -413,41 +422,190 @@ DOMHighResTimeStamp Performance::ResolveTimestampFromName( 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) { nsAutoCString attributeName; switch (aAttribute) { case ResolveTimestampAttribute::Start: attributeName = "start"; break; case ResolveTimestampAttribute::End: attributeName = "end"; break; case ResolveTimestampAttribute::Duration: attributeName = "duration"; break; } nsPrintfCString errorMsg("Given attribute %s cannot be negative", attributeName.get()); aRv.ThrowTypeError(errorMsg); } 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, ErrorResult& aRv) { // We add nothing when 'privacy.resistFingerprinting' is on. if (nsContentUtils::ShouldResistFingerprinting()) { return; const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { DOMHighResTimeStamp endTime; if (aEndMark.WasPassed()) { endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv); } else if (aOptions && aOptions->mEnd.WasPassed()) { endTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); } else if (aOptions && 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 Maybe<const nsAString&>& aStartMark, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { DOMHighResTimeStamp startTime; DOMHighResTimeStamp endTime; if (aOptions && aOptions->mStart.WasPassed()) { startTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::Start, aOptions->mStart.Value(), aRv); } else if (aOptions && aOptions->mDuration.WasPassed() && aOptions->mEnd.WasPassed()) { const DOMHighResTimeStamp duration = ConvertMarkToTimestampWithDOMHighResTimeStamp( ResolveTimestampAttribute::Duration, aOptions->mDuration.Value(), aRv); if (aRv.Failed()) { return 0; } if (aStartMark.WasPassed()) { startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); if (NS_WARN_IF(aRv.Failed())) { return; const DOMHighResTimeStamp end = ConvertMarkToTimestamp( ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); if (aRv.Failed()) { return 0; } startTime = end - duration; } else if (aStartMark) { startTime = ConvertMarkToTimestampWithString(*aStartMark, aRv); } 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; } return startTime; } already_AddRefed<PerformanceMeasure> Performance::Measure( JSContext* aCx, const nsAString& aName, const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, const Optional<nsAString>& aEndMark, ErrorResult& aRv) { // When resisting fingerprinting, we don't add marks to the buffer. Since // measure relies on relationships between marks in the buffer, this method // will throw if we look for user-entered marks so we return a dummy measure // instead of continuing. We could instead return real values for performance // timing attributes and dummy values for user-entered marks but this adds // complexity that doesn't seem worth the effort because these fingerprinting // protections may not longer be necessary (since performance.now() already // has reduced precision). if (nsContentUtils::ShouldResistFingerprinting()) { return do_AddRef(new PerformanceMeasure(GetParentObject(), aName, 0, 0, JS::NullHandleValue)); } // Maybe is more readable than using the union type directly. Maybe<const PerformanceMeasureOptions&> options; if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) { options.emplace(aStartOrMeasureOptions.GetAsPerformanceMeasureOptions()); } const bool isOptionsNotEmpty = options.isSome() && (!options->mDetail.isUndefined() || options->mStart.WasPassed() || options->mEnd.WasPassed() || options->mDuration.WasPassed()); if (isOptionsNotEmpty) { if (aEndMark.WasPassed()) { endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); aRv.ThrowTypeError( "Cannot provide separate endMark argument if " "PerformanceMeasureOptions argument is given"); return nullptr; } if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) { aRv.ThrowTypeError( "PerformanceMeasureOptions must have start and/or end member"); return nullptr; } if (options->mStart.WasPassed() && options->mDuration.WasPassed() && options->mEnd.WasPassed()) { aRv.ThrowTypeError( "PerformanceMeasureOptions cannot have all of the following members: " "start, duration, and end"); return nullptr; } } const DOMHighResTimeStamp endTime = ResolveEndTimeForMeasure(aEndMark, options, aRv); if (NS_WARN_IF(aRv.Failed())) { return; return nullptr; } // Convert to Maybe for consistency with options. Maybe<const nsAString&> startMark; if (aStartOrMeasureOptions.IsString()) { startMark.emplace(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 && !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(GetParentObject(), aName, startTime, endTime); RefPtr<PerformanceMeasure> performanceMeasure = new PerformanceMeasure( GetParentObject(), aName, startTime, endTime, detail); InsertUserEntry(performanceMeasure); if (profiler_thread_is_being_profiled_for_markers()) { Loading @@ -456,12 +614,6 @@ void Performance::Measure(const nsAString& aName, TimeStamp endTimeStamp = CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime); // Convert to Maybe values so that Optional types do not need to be used in // the profiler. Maybe<nsString> startMark; if (aStartMark.WasPassed()) { startMark.emplace(aStartMark.Value()); } Maybe<nsString> endMark; if (aEndMark.WasPassed()) { endMark.emplace(aEndMark.Value()); Loading @@ -477,6 +629,8 @@ void Performance::Measure(const nsAString& aName, UserTimingMarker{}, aName, /* aIsMeasure */ true, startMark, endMark); } return performanceMeasure.forget(); } void Performance::ClearMeasures(const Optional<nsAString>& aName) { Loading dom/performance/Performance.h +30 −5 Original line number Diff line number Diff line Loading @@ -21,9 +21,13 @@ class ErrorResult; namespace dom { class OwningStringOrDouble; class StringOrPerformanceMeasureOptions; class PerformanceEntry; class PerformanceMark; struct PerformanceMarkOptions; struct PerformanceMeasureOptions; class PerformanceMeasure; class PerformanceNavigation; class PerformancePaintTiming; class PerformanceObserver; Loading Loading @@ -84,7 +88,9 @@ class Performance : public DOMEventTargetHelper { void ClearMarks(const Optional<nsAString>& aName); void Measure(const nsAString& aName, const Optional<nsAString>& aStartMark, already_AddRefed<PerformanceMeasure> Measure( JSContext* aCx, const nsAString& aName, const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, const Optional<nsAString>& aEndMark, ErrorResult& aRv); void ClearMeasures(const Optional<nsAString>& aName); Loading Loading @@ -161,9 +167,6 @@ class Performance : public DOMEventTargetHelper { void ClearUserEntries(const Optional<nsAString>& aEntryName, const nsAString& aEntryType); DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, ErrorResult& aRv); virtual void DispatchBufferFullEvent() = 0; virtual DOMHighResTimeStamp CreationTime() const = 0; Loading Loading @@ -206,6 +209,28 @@ class Performance : public DOMEventTargetHelper { private: MOZ_ALWAYS_INLINE bool CanAddResourceTimingEntry(); void BufferEvent(); // 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 Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv); DOMHighResTimeStamp ResolveStartTimeForMeasure( const Maybe<const nsAString&>& aStartMark, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv); }; } // namespace dom Loading dom/performance/PerformanceMark.cpp +4 −3 Original line number Diff line number Diff line Loading @@ -100,9 +100,10 @@ JSObject* PerformanceMark::WrapObject(JSContext* aCx, void PerformanceMark::GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) { // Return a copy so that the PerformanceMark.detail reference always returns // the same value. However, the contents of detail can be mutated. The spec // isn't clear if this is okay but it matches Chrome's behavior. // 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. aRetval.set(mDetail); } Loading dom/performance/PerformanceMeasure.cpp +34 −3 Original line number Diff line number Diff line Loading @@ -13,18 +13,49 @@ using namespace mozilla::dom; PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aEndTime) DOMHighResTimeStamp aEndTime, const JS::Handle<JS::Value>& aDetail) : PerformanceEntry(aParent, aName, u"measure"_ns), mStartTime(aStartTime), mDuration(aEndTime - aStartTime) {} mDuration(aEndTime - aStartTime), mDetail(aDetail) { mozilla::HoldJSObjects(this); } 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 PerformanceMeasure::~PerformanceMeasure() = default; 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_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(PerformanceMeasure, PerformanceEntry) JSObject* PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return PerformanceMeasure_Binding::Wrap(aCx, this, aGivenProto); } void PerformanceMeasure::GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) { // 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. aRetval.set(mDetail); } size_t PerformanceMeasure::SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); Loading dom/performance/PerformanceMeasure.h +11 −1 Original line number Diff line number Diff line Loading @@ -14,9 +14,14 @@ namespace mozilla::dom { // http://www.w3.org/TR/user-timing/#performancemeasure 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; Loading @@ -25,6 +30,8 @@ class PerformanceMeasure final : public PerformanceEntry { virtual DOMHighResTimeStamp Duration() const override { return mDuration; } void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval); size_t SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const override; Loading @@ -32,6 +39,9 @@ class PerformanceMeasure final : public PerformanceEntry { virtual ~PerformanceMeasure(); DOMHighResTimeStamp mStartTime; DOMHighResTimeStamp mDuration; private: JS::Heap<JS::Value> mDetail; }; } // namespace mozilla::dom Loading Loading
dom/performance/Performance.cpp +184 −30 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include "PerformanceWorker.h" #include "mozilla/BasePrincipal.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/PerformanceBinding.h" #include "mozilla/dom/PerformanceEntryEvent.h" #include "mozilla/dom/PerformanceNavigationBinding.h" Loading @@ -33,6 +34,12 @@ namespace mozilla::dom { enum class Performance::ResolveTimestampAttribute { Start, End, Duration, }; NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Performance) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) Loading Loading @@ -387,7 +394,7 @@ void Performance::ClearMarks(const Optional<nsAString>& aName) { ClearUserEntries(aName, u"mark"_ns); } DOMHighResTimeStamp Performance::ResolveTimestampFromName( DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString( const nsAString& aName, ErrorResult& aRv) { AutoTArray<RefPtr<PerformanceEntry>, 1> arr; Optional<nsAString> typeParam; Loading @@ -400,7 +407,9 @@ DOMHighResTimeStamp Performance::ResolveTimestampFromName( } if (!IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); nsPrintfCString errorMsg("Given mark name, %s, is unknown", NS_ConvertUTF16toUTF8(aName).get()); aRv.ThrowSyntaxError(errorMsg); return 0; } Loading @@ -413,41 +422,190 @@ DOMHighResTimeStamp Performance::ResolveTimestampFromName( 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) { nsAutoCString attributeName; switch (aAttribute) { case ResolveTimestampAttribute::Start: attributeName = "start"; break; case ResolveTimestampAttribute::End: attributeName = "end"; break; case ResolveTimestampAttribute::Duration: attributeName = "duration"; break; } nsPrintfCString errorMsg("Given attribute %s cannot be negative", attributeName.get()); aRv.ThrowTypeError(errorMsg); } 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, ErrorResult& aRv) { // We add nothing when 'privacy.resistFingerprinting' is on. if (nsContentUtils::ShouldResistFingerprinting()) { return; const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { DOMHighResTimeStamp endTime; if (aEndMark.WasPassed()) { endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv); } else if (aOptions && aOptions->mEnd.WasPassed()) { endTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); } else if (aOptions && 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 Maybe<const nsAString&>& aStartMark, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { DOMHighResTimeStamp startTime; DOMHighResTimeStamp endTime; if (aOptions && aOptions->mStart.WasPassed()) { startTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::Start, aOptions->mStart.Value(), aRv); } else if (aOptions && aOptions->mDuration.WasPassed() && aOptions->mEnd.WasPassed()) { const DOMHighResTimeStamp duration = ConvertMarkToTimestampWithDOMHighResTimeStamp( ResolveTimestampAttribute::Duration, aOptions->mDuration.Value(), aRv); if (aRv.Failed()) { return 0; } if (aStartMark.WasPassed()) { startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); if (NS_WARN_IF(aRv.Failed())) { return; const DOMHighResTimeStamp end = ConvertMarkToTimestamp( ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); if (aRv.Failed()) { return 0; } startTime = end - duration; } else if (aStartMark) { startTime = ConvertMarkToTimestampWithString(*aStartMark, aRv); } 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; } return startTime; } already_AddRefed<PerformanceMeasure> Performance::Measure( JSContext* aCx, const nsAString& aName, const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, const Optional<nsAString>& aEndMark, ErrorResult& aRv) { // When resisting fingerprinting, we don't add marks to the buffer. Since // measure relies on relationships between marks in the buffer, this method // will throw if we look for user-entered marks so we return a dummy measure // instead of continuing. We could instead return real values for performance // timing attributes and dummy values for user-entered marks but this adds // complexity that doesn't seem worth the effort because these fingerprinting // protections may not longer be necessary (since performance.now() already // has reduced precision). if (nsContentUtils::ShouldResistFingerprinting()) { return do_AddRef(new PerformanceMeasure(GetParentObject(), aName, 0, 0, JS::NullHandleValue)); } // Maybe is more readable than using the union type directly. Maybe<const PerformanceMeasureOptions&> options; if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) { options.emplace(aStartOrMeasureOptions.GetAsPerformanceMeasureOptions()); } const bool isOptionsNotEmpty = options.isSome() && (!options->mDetail.isUndefined() || options->mStart.WasPassed() || options->mEnd.WasPassed() || options->mDuration.WasPassed()); if (isOptionsNotEmpty) { if (aEndMark.WasPassed()) { endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); aRv.ThrowTypeError( "Cannot provide separate endMark argument if " "PerformanceMeasureOptions argument is given"); return nullptr; } if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) { aRv.ThrowTypeError( "PerformanceMeasureOptions must have start and/or end member"); return nullptr; } if (options->mStart.WasPassed() && options->mDuration.WasPassed() && options->mEnd.WasPassed()) { aRv.ThrowTypeError( "PerformanceMeasureOptions cannot have all of the following members: " "start, duration, and end"); return nullptr; } } const DOMHighResTimeStamp endTime = ResolveEndTimeForMeasure(aEndMark, options, aRv); if (NS_WARN_IF(aRv.Failed())) { return; return nullptr; } // Convert to Maybe for consistency with options. Maybe<const nsAString&> startMark; if (aStartOrMeasureOptions.IsString()) { startMark.emplace(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 && !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(GetParentObject(), aName, startTime, endTime); RefPtr<PerformanceMeasure> performanceMeasure = new PerformanceMeasure( GetParentObject(), aName, startTime, endTime, detail); InsertUserEntry(performanceMeasure); if (profiler_thread_is_being_profiled_for_markers()) { Loading @@ -456,12 +614,6 @@ void Performance::Measure(const nsAString& aName, TimeStamp endTimeStamp = CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime); // Convert to Maybe values so that Optional types do not need to be used in // the profiler. Maybe<nsString> startMark; if (aStartMark.WasPassed()) { startMark.emplace(aStartMark.Value()); } Maybe<nsString> endMark; if (aEndMark.WasPassed()) { endMark.emplace(aEndMark.Value()); Loading @@ -477,6 +629,8 @@ void Performance::Measure(const nsAString& aName, UserTimingMarker{}, aName, /* aIsMeasure */ true, startMark, endMark); } return performanceMeasure.forget(); } void Performance::ClearMeasures(const Optional<nsAString>& aName) { Loading
dom/performance/Performance.h +30 −5 Original line number Diff line number Diff line Loading @@ -21,9 +21,13 @@ class ErrorResult; namespace dom { class OwningStringOrDouble; class StringOrPerformanceMeasureOptions; class PerformanceEntry; class PerformanceMark; struct PerformanceMarkOptions; struct PerformanceMeasureOptions; class PerformanceMeasure; class PerformanceNavigation; class PerformancePaintTiming; class PerformanceObserver; Loading Loading @@ -84,7 +88,9 @@ class Performance : public DOMEventTargetHelper { void ClearMarks(const Optional<nsAString>& aName); void Measure(const nsAString& aName, const Optional<nsAString>& aStartMark, already_AddRefed<PerformanceMeasure> Measure( JSContext* aCx, const nsAString& aName, const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions, const Optional<nsAString>& aEndMark, ErrorResult& aRv); void ClearMeasures(const Optional<nsAString>& aName); Loading Loading @@ -161,9 +167,6 @@ class Performance : public DOMEventTargetHelper { void ClearUserEntries(const Optional<nsAString>& aEntryName, const nsAString& aEntryType); DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, ErrorResult& aRv); virtual void DispatchBufferFullEvent() = 0; virtual DOMHighResTimeStamp CreationTime() const = 0; Loading Loading @@ -206,6 +209,28 @@ class Performance : public DOMEventTargetHelper { private: MOZ_ALWAYS_INLINE bool CanAddResourceTimingEntry(); void BufferEvent(); // 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 Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv); DOMHighResTimeStamp ResolveStartTimeForMeasure( const Maybe<const nsAString&>& aStartMark, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv); }; } // namespace dom Loading
dom/performance/PerformanceMark.cpp +4 −3 Original line number Diff line number Diff line Loading @@ -100,9 +100,10 @@ JSObject* PerformanceMark::WrapObject(JSContext* aCx, void PerformanceMark::GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) { // Return a copy so that the PerformanceMark.detail reference always returns // the same value. However, the contents of detail can be mutated. The spec // isn't clear if this is okay but it matches Chrome's behavior. // 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. aRetval.set(mDetail); } Loading
dom/performance/PerformanceMeasure.cpp +34 −3 Original line number Diff line number Diff line Loading @@ -13,18 +13,49 @@ using namespace mozilla::dom; PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aEndTime) DOMHighResTimeStamp aEndTime, const JS::Handle<JS::Value>& aDetail) : PerformanceEntry(aParent, aName, u"measure"_ns), mStartTime(aStartTime), mDuration(aEndTime - aStartTime) {} mDuration(aEndTime - aStartTime), mDetail(aDetail) { mozilla::HoldJSObjects(this); } 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 PerformanceMeasure::~PerformanceMeasure() = default; 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_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(PerformanceMeasure, PerformanceEntry) JSObject* PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return PerformanceMeasure_Binding::Wrap(aCx, this, aGivenProto); } void PerformanceMeasure::GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) { // 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. aRetval.set(mDetail); } size_t PerformanceMeasure::SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); Loading
dom/performance/PerformanceMeasure.h +11 −1 Original line number Diff line number Diff line Loading @@ -14,9 +14,14 @@ namespace mozilla::dom { // http://www.w3.org/TR/user-timing/#performancemeasure 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; Loading @@ -25,6 +30,8 @@ class PerformanceMeasure final : public PerformanceEntry { virtual DOMHighResTimeStamp Duration() const override { return mDuration; } void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval); size_t SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const override; Loading @@ -32,6 +39,9 @@ class PerformanceMeasure final : public PerformanceEntry { virtual ~PerformanceMeasure(); DOMHighResTimeStamp mStartTime; DOMHighResTimeStamp mDuration; private: JS::Heap<JS::Value> mDetail; }; } // namespace mozilla::dom Loading