summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2023-04-11 21:32:22 +0000
committerMoonchild <moonchild@palemoon.org>2023-04-11 21:32:22 +0000
commitc95e970df58fafc176cdeedac12c1f6199c301cd (patch)
tree481b67d375e2eae59319f3caa7a90adc2a92932a
parentff29aa37a86fcbc97cba6870d7bdb50020b442ae (diff)
parent9e976ddf7f2591a8abf4b79baf58627939a7c5eb (diff)
downloaduxp-c95e970df58fafc176cdeedac12c1f6199c301cd.tar.gz
Merge pull request 'Update Performance API to User Timing L3' (#2208) from FranklinDM/UXP-contrib:work_js-user-timing-l3 into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/2208
-rw-r--r--dom/base/domerr.msg6
-rw-r--r--dom/bindings/Bindings.conf6
-rw-r--r--dom/bindings/Errors.msg7
-rw-r--r--dom/bindings/Exceptions.cpp1
-rwxr-xr-xdom/performance/Performance.cpp314
-rw-r--r--dom/performance/Performance.h64
-rw-r--r--dom/performance/PerformanceMainThread.cpp22
-rw-r--r--dom/performance/PerformanceMainThread.h7
-rw-r--r--dom/performance/PerformanceMark.cpp96
-rw-r--r--dom/performance/PerformanceMark.h28
-rw-r--r--dom/performance/PerformanceMeasure.cpp45
-rw-r--r--dom/performance/PerformanceMeasure.h12
-rw-r--r--dom/webidl/Performance.webidl22
-rw-r--r--dom/webidl/PerformanceMark.webidl3
-rw-r--r--dom/webidl/PerformanceMeasure.webidl1
-rw-r--r--xpcom/base/ErrorList.h10
-rw-r--r--xpcom/base/nsError.h1
17 files changed, 552 insertions, 93 deletions
diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg
index 9c84f42a3f..baf1f05735 100644
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -157,6 +157,12 @@ DOM4_MSG_DEF(NotSupportedError, "The media resource indicated by the src attribu
DOM4_MSG_DEF(SyntaxError, "The URI is malformed.", NS_ERROR_DOM_MALFORMED_URI)
DOM4_MSG_DEF(SyntaxError, "Invalid header name.", NS_ERROR_DOM_INVALID_HEADER_NAME)
+/* User Timing API errors */
+DOM4_MSG_DEF(SyntaxError, "An unknown mark name was provided.", NS_ERROR_DOM_UT_UNKNOWN_MARK_NAME)
+DOM4_MSG_DEF(SyntaxError, "markName cannot be a performance timing attribute.", NS_ERROR_DOM_UT_INVALID_TIMING_ATTR)
+DOM4_MSG_DEF(InvalidAccessError, "A PerformanceTiming attribute which isn't available yet was provided.", NS_ERROR_DOM_UT_UNAVAILABLE_ATTR)
+DOM4_MSG_DEF(InvalidStateError, "Global object is unavailable.", NS_ERROR_DOM_UT_UNAVAILABLE_GLOBAL_OBJECT)
+
/* XMLHttpRequest errors. */
DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest has an invalid context.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT)
DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must be OPENED.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED)
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index 146bf8d15b..8b13babdbb 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -642,6 +642,12 @@ DOMInterfaces = {
'wrapperCache': False
},
+'Performance' : {
+ 'implicitJSContext': [
+ 'mark'
+ ],
+},
+
'Plugin': {
'headerFile' : 'nsPluginArray.h',
'nativeType': 'nsPluginElement',
diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg
index c47f758750..c894c6c7b4 100644
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -100,3 +100,10 @@ MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the suppo
MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.")
+MSG_DEF(MSG_NO_NEGATIVE_ATTR, 1, JSEXN_TYPEERR, "Given attribute {0} cannot be negative.")
+MSG_DEF(MSG_PMO_NO_SEPARATE_ENDMARK, 0, JSEXN_TYPEERR, "Cannot provide separate endMark argument if PerformanceMeasureOptions argument is given.")
+MSG_DEF(MSG_PMO_MISSING_STARTENDMARK, 0, JSEXN_TYPEERR, "PerformanceMeasureOptions must have start and/or end member.")
+MSG_DEF(MSG_PMO_INVALID_MEMBERS, 0, JSEXN_TYPEERR, "PerformanceMeasureOptions cannot have all of the following members: start, duration, and end.")
+MSG_DEF(MSG_PMO_CONSTRUCTOR_INACCESSIBLE, 0, JSEXN_TYPEERR, "Can't access PerformanceMark constructor, performance is null.")
+MSG_DEF(MSG_PMO_UNEXPECTED_START_TIME, 0, JSEXN_TYPEERR, "Expected startTime >= 0.")
+MSG_DEF(MSG_PMO_INVALID_ATTR_FOR_NON_GLOBAL, 1, JSEXN_TYPEERR, "Cannot get PerformanceTiming attribute values for non-Window global object. Given: {0}.")
diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp
index 1785010cc5..1d361740a2 100644
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -195,6 +195,7 @@ CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
case NS_ERROR_MODULE_DOM_ANIM:
case NS_ERROR_MODULE_DOM_PUSH:
case NS_ERROR_MODULE_DOM_MEDIA:
+ case NS_ERROR_MODULE_DOM_USER_TIMING:
if (aMessage.IsEmpty()) {
return DOMException::Create(aRv);
}
diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp
index 020dc0d89a..9a1d134626 100755
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -20,11 +20,13 @@
#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"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
+#include "WorkerScope.h"
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
@@ -65,6 +67,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)
@@ -98,6 +106,27 @@ Performance::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate)
return performance.forget();
}
+/* static */
+already_AddRefed<Performance> Performance::Get(JSContext* aCx,
+ nsIGlobalObject* aGlobal) {
+ RefPtr<Performance> performance;
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+ if (window) {
+ performance = window->GetPerformance();
+ } else {
+ const WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ if (!workerPrivate) {
+ return nullptr;
+ }
+
+ WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+ MOZ_ASSERT(scope);
+ performance = scope->GetPerformance();
+ }
+
+ return performance.forget();
+}
+
Performance::Performance()
: mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
, mPendingNotificationObserversTask(false)
@@ -227,27 +256,42 @@ Performance::RoundTime(double aTime) const
return floor(aTime / maxResolutionMs) * maxResolutionMs;
}
-
-void
-Performance::Mark(const nsAString& aName, ErrorResult& aRv)
+already_AddRefed<PerformanceMark> Performance::Mark(
+ JSContext* aCx,
+ const nsAString& aName,
+ const PerformanceMarkOptions& aMarkOptions,
+ 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;
}
- if (IsPerformanceTimingAttribute(aName)) {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
+ nsCOMPtr<nsIGlobalObject> parent = GetParentObject();
+ if (!parent || parent->IsDying() || !parent->GetGlobalJSObject()) {
+ aRv.Throw(NS_ERROR_DOM_UT_UNAVAILABLE_GLOBAL_OBJECT);
+ return nullptr;
+ }
+
+ GlobalObject global(aCx, parent->GetGlobalJSObject());
+ if (global.Failed()) {
+ aRv.Throw(NS_ERROR_DOM_UT_UNAVAILABLE_GLOBAL_OBJECT);
+ return nullptr;
}
RefPtr<PerformanceMark> performanceMark =
- new PerformanceMark(GetAsISupports(), aName, Now());
+ PerformanceMark::Constructor(global, aName, aMarkOptions, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
InsertUserEntry(performanceMark);
if (profiler_is_active()) {
PROFILER_MARKER(NS_ConvertUTF16toUTF8(aName).get());
}
+
+ return performanceMark.forget();
}
void
@@ -256,12 +300,37 @@ Performance::ClearMarks(const Optional<nsAString>& aName)
ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
}
+// To be removed once bug 1124165 lands
+bool
+Performance::IsPerformanceTimingAttribute(const nsAString& aName) const
+{
+ // Note that toJSON is added to this list due to bug 1047848
+ static const char* attributes[] =
+ {"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
+ "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
+ "connectStart", "secureConnectionStart", "connectEnd", "requestStart", "responseStart",
+ "responseEnd", "domLoading", "domInteractive",
+ "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
+ "loadEventStart", "loadEventEnd", nullptr};
+
+ for (uint32_t i = 0; attributes[i]; ++i) {
+ if (aName.EqualsASCII(attributes[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
DOMHighResTimeStamp
-Performance::ResolveTimestampFromName(const nsAString& aName,
- ErrorResult& aRv)
+Performance::ConvertMarkToTimestampWithString(const nsAString& aName,
+ ErrorResult& aRv)
{
+ if (IsPerformanceTimingAttribute(aName)) {
+ return ConvertNameToTimestamp(aName, aRv);
+ }
+
AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
- DOMHighResTimeStamp ts;
Optional<nsAString> typeParam;
nsAutoString str;
str.AssignLiteral("mark");
@@ -271,64 +340,223 @@ Performance::ResolveTimestampFromName(const nsAString& aName,
return arr.LastElement()->StartTime();
}
- if (!IsPerformanceTimingAttribute(aName)) {
- aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ aRv.Throw(NS_ERROR_DOM_UT_UNKNOWN_MARK_NAME);
+ return 0;
+}
+
+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::ConvertNameToTimestamp(const nsAString& aName,
+ ErrorResult& aRv) {
+ if (!IsGlobalObjectWindow()) {
+ aRv.ThrowTypeError<MSG_PMO_INVALID_ATTR_FOR_NON_GLOBAL>(aName);
return 0;
}
- ts = GetPerformanceTimingFromString(aName);
- if (!ts) {
- aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+ if (aName.EqualsASCII("navigationStart")) {
return 0;
}
- return ts - CreationTime();
+ // We use GetPerformanceTimingFromString, rather than calling the
+ // navigationStart method timing function directly, because the former handles
+ // reducing precision against timing attacks.
+ const DOMHighResTimeStamp startTime =
+ GetPerformanceTimingFromString(NS_LITERAL_STRING("navigationStart"));
+ const DOMHighResTimeStamp endTime =
+ GetPerformanceTimingFromString(aName);
+ MOZ_ASSERT(endTime >= 0);
+ if (endTime == 0) {
+ aRv.Throw(NS_ERROR_DOM_UT_UNAVAILABLE_ATTR);
+ return 0;
+ }
+
+ return endTime - startTime;
}
-void
-Performance::Measure(const nsAString& aName,
- const Optional<nsAString>& aStartMark,
+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..1b2bb5586c 100644
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -20,11 +20,18 @@ class ErrorResult;
namespace dom {
+class OwningStringOrDouble;
+class StringOrPerformanceMeasureOptions;
class PerformanceEntry;
+class PerformanceMark;
+struct PerformanceMarkOptions;
+struct PerformanceMeasureOptions;
+class PerformanceMeasure;
class PerformanceNavigation;
class PerformanceObserver;
class PerformanceService;
class PerformanceTiming;
+struct StructuredSerializeOptions;
namespace workers {
class WorkerPrivate;
@@ -50,6 +57,10 @@ public:
static already_AddRefed<Performance>
CreateForWorker(workers::WorkerPrivate* aWorkerPrivate);
+ // This will return nullptr if called outside of a Window or Worker.
+ static already_AddRefed<Performance> Get(JSContext* aCx,
+ nsIGlobalObject* aGlobal);
+
JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> aGivenProto) override;
@@ -71,14 +82,16 @@ public:
DOMHighResTimeStamp TimeOrigin();
- void Mark(const nsAString& aName, ErrorResult& aRv);
+ already_AddRefed<PerformanceMark> Mark(
+ JSContext* aCx, const nsAString& aName,
+ const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv);
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);
@@ -104,6 +117,13 @@ public:
virtual nsITimedChannel* GetChannel() const = 0;
+ bool IsPerformanceTimingAttribute(const nsAString& aName) const;
+
+ virtual bool IsGlobalObjectWindow() const
+ {
+ return false;
+ }
+
protected:
Performance();
explicit Performance(nsPIDOMWindowInner* aWindow);
@@ -116,9 +136,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;
@@ -127,11 +144,6 @@ protected:
virtual DOMHighResTimeStamp CreationTime() const = 0;
- virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
- {
- return false;
- }
-
virtual DOMHighResTimeStamp
GetPerformanceTimingFromString(const nsAString& aTimingName)
{
@@ -163,6 +175,32 @@ 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 ConvertNameToTimestamp(const nsAString& aName,
+ 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/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp
index acb69ea358..a4dbf88799 100644
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -177,28 +177,6 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
}
}
-// To be removed once bug 1124165 lands
-bool
-PerformanceMainThread::IsPerformanceTimingAttribute(const nsAString& aName)
-{
- // Note that toJSON is added to this list due to bug 1047848
- static const char* attributes[] =
- {"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
- "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
- "connectStart", "secureConnectionStart", "connectEnd", "requestStart", "responseStart",
- "responseEnd", "domLoading", "domInteractive",
- "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
- "loadEventStart", "loadEventEnd", nullptr};
-
- for (uint32_t i = 0; attributes[i]; ++i) {
- if (aName.EqualsASCII(attributes[i])) {
- return true;
- }
- }
-
- return false;
-}
-
DOMHighResTimeStamp
PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
{
diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h
index 35fd4ab0e2..702483e9de 100644
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -57,6 +57,11 @@ public:
const Optional<nsAString>& aEntryType,
nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
+ bool IsGlobalObjectWindow() const override
+ {
+ return true;
+ }
+
protected:
~PerformanceMainThread();
@@ -67,8 +72,6 @@ protected:
void InsertUserEntry(PerformanceEntry* aEntry) override;
- bool IsPerformanceTimingAttribute(const nsAString& aName) override;
-
DOMHighResTimeStamp
GetPerformanceTimingFromString(const nsAString& aTimingName) override;
diff --git a/dom/performance/PerformanceMark.cpp b/dom/performance/PerformanceMark.cpp
index c37d057b66..771fc64467 100644
--- a/dom/performance/PerformanceMark.cpp
+++ b/dom/performance/PerformanceMark.cpp
@@ -6,25 +6,119 @@
#include "PerformanceMark.h"
#include "MainThreadUtils.h"
#include "mozilla/dom/PerformanceMarkBinding.h"
+#include "mozilla/dom/MessagePortBinding.h"
+using mozilla::dom::StructuredSerializeOptions;
using namespace mozilla::dom;
PerformanceMark::PerformanceMark(nsISupports* aParent,
const nsAString& aName,
- DOMHighResTimeStamp aStartTime)
+ DOMHighResTimeStamp aStartTime,
+ const JS::Handle<JS::Value>& aDetail)
: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("mark"))
, mStartTime(aStartTime)
+ , mDetail(aDetail)
{
// mParent is null in workers.
MOZ_ASSERT(mParent || !NS_IsMainThread());
+ mozilla::HoldJSObjects(this);
+}
+
+
+already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
+ const GlobalObject& aGlobal,
+ const nsAString& aMarkName,
+ const PerformanceMarkOptions& aMarkOptions,
+ ErrorResult& aRv)
+{
+ const nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(aGlobal.GetAsSupports());
+ return PerformanceMark::Constructor(aGlobal.Context(), global, aMarkName,
+ aMarkOptions, aRv);
+}
+
+already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
+ JSContext* aCx,
+ nsIGlobalObject* aGlobal,
+ const nsAString& aMarkName,
+ const PerformanceMarkOptions& aMarkOptions,
+ ErrorResult& aRv)
+{
+ RefPtr<Performance> performance = Performance::Get(aCx, aGlobal);
+ if (!performance) {
+ // This is similar to the message that occurs when accessing `performance`
+ // from outside a valid global.
+ aRv.ThrowTypeError<MSG_PMO_CONSTRUCTOR_INACCESSIBLE>();
+ return nullptr;
+ }
+
+ if (performance->IsGlobalObjectWindow() &&
+ performance->IsPerformanceTimingAttribute(aMarkName)) {
+ aRv.Throw(NS_ERROR_DOM_UT_INVALID_TIMING_ATTR);
+ return nullptr;
+ }
+
+ DOMHighResTimeStamp startTime = aMarkOptions.mStartTime.WasPassed() ?
+ aMarkOptions.mStartTime.Value() :
+ performance->Now();
+ if (startTime < 0) {
+ aRv.ThrowTypeError<MSG_PMO_UNEXPECTED_START_TIME>();
+ return nullptr;
+ }
+
+ JS::Rooted<JS::Value> detail(aCx);
+ if (aMarkOptions.mDetail.isNullOrUndefined()) {
+ detail.setNull();
+ } else {
+ StructuredSerializeOptions serializeOptions;
+ JS::Rooted<JS::Value> valueToClone(aCx, aMarkOptions.mDetail);
+ nsContentUtils::StructuredClone(aCx, aGlobal, valueToClone,
+ serializeOptions, &detail, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ }
+
+ return do_AddRef(new PerformanceMark(aGlobal, aMarkName, startTime, detail));
}
PerformanceMark::~PerformanceMark()
{
+ mozilla::DropJSObjects(this);
}
+NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMark)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMark,
+ PerformanceEntry)
+ tmp->mDetail.setUndefined();
+ mozilla::DropJSObjects(tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMark,
+ PerformanceEntry)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMark,
+ PerformanceEntry)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDetail)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMark)
+NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
+NS_IMPL_ADDREF_INHERITED(PerformanceMark, PerformanceEntry)
+NS_IMPL_RELEASE_INHERITED(PerformanceMark, PerformanceEntry)
+
JSObject*
PerformanceMark::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return PerformanceMarkBinding::Wrap(aCx, this, aGivenProto);
}
+
+void PerformanceMark::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/PerformanceMark.h b/dom/performance/PerformanceMark.h
index a080af6a82..4e98bfaa92 100644
--- a/dom/performance/PerformanceMark.h
+++ b/dom/performance/PerformanceMark.h
@@ -11,16 +11,40 @@
namespace mozilla {
namespace dom {
+struct PerformanceMarkOptions;
+
// http://www.w3.org/TR/user-timing/#performancemark
class PerformanceMark final : public PerformanceEntry
{
-public:
+private:
PerformanceMark(nsISupports* aParent,
const nsAString& aName,
- DOMHighResTimeStamp aStartTime);
+ DOMHighResTimeStamp aStartTime,
+ const JS::Handle<JS::Value>& aDetail);
+
+ JS::Heap<JS::Value> mDetail;
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMark,
+ PerformanceEntry);
+
+ static already_AddRefed<PerformanceMark> Constructor(
+ const GlobalObject& aGlobal,
+ const nsAString& aMarkName,
+ const PerformanceMarkOptions& aMarkOptions,
+ ErrorResult& aRv);
+
+ static already_AddRefed<PerformanceMark> Constructor(
+ JSContext* aCx, nsIGlobalObject* aGlobal,
+ const nsAString& aMarkName,
+ const PerformanceMarkOptions& aMarkOptions,
+ ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+ void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRv);
+
virtual DOMHighResTimeStamp StartTime() const override
{
return mStartTime;
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
diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl
index e811e1cee2..7cd6c67172 100644
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -14,7 +14,7 @@ typedef double DOMHighResTimeStamp;
typedef sequence <PerformanceEntry> PerformanceEntryList;
[Exposed=(Window,Worker)]
-interface Performance {
+interface Performance : EventTarget {
[DependsOn=DeviceState, Affects=Nothing]
DOMHighResTimeStamp now();
@@ -44,6 +44,14 @@ partial interface Performance {
entryType);
};
+// https://w3c.github.io/user-timing/#extensions-performance-interface
+dictionary PerformanceMeasureOptions {
+ any detail;
+ (DOMString or DOMHighResTimeStamp) start;
+ DOMHighResTimeStamp duration;
+ (DOMString or DOMHighResTimeStamp)end;
+};
+
// http://www.w3.org/TR/resource-timing/#extensions-performance-interface
[Exposed=Window]
partial interface Performance {
@@ -64,15 +72,23 @@ partial interface Performance {
};
#endif
+// https://w3c.github.io/user-timing/#extensions-performance-interface
+dictionary PerformanceMarkOptions {
+ any detail;
+ DOMHighResTimeStamp startTime;
+};
+
// http://www.w3.org/TR/user-timing/
[Exposed=(Window,Worker)]
partial interface Performance {
[Func="Performance::IsEnabled", Throws]
- void mark(DOMString markName);
+ PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions);
[Func="Performance::IsEnabled"]
void clearMarks(optional DOMString markName);
[Func="Performance::IsEnabled", Throws]
- void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);
+ PerformanceMeasure measure(DOMString measureName,
+ optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions,
+ optional DOMString endMark);
[Func="Performance::IsEnabled"]
void clearMeasures(optional DOMString measureName);
};
diff --git a/dom/webidl/PerformanceMark.webidl b/dom/webidl/PerformanceMark.webidl
index 20e9e92c02..2ef9fcd844 100644
--- a/dom/webidl/PerformanceMark.webidl
+++ b/dom/webidl/PerformanceMark.webidl
@@ -7,7 +7,8 @@
* http://www.w3.org/TR/user-timing/#performancemark
*/
-[Exposed=(Window,Worker)]
+[Exposed=(Window,Worker), Constructor(DOMString markName, optional PerformanceMarkOptions markOptions)]
interface PerformanceMark : PerformanceEntry
{
+ readonly attribute any detail;
};
diff --git a/dom/webidl/PerformanceMeasure.webidl b/dom/webidl/PerformanceMeasure.webidl
index aa4e8cd256..a520778b88 100644
--- a/dom/webidl/PerformanceMeasure.webidl
+++ b/dom/webidl/PerformanceMeasure.webidl
@@ -10,4 +10,5 @@
[Exposed=(Window,Worker)]
interface PerformanceMeasure : PerformanceEntry
{
+ readonly attribute any detail;
};
diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h
index 980171cb92..8ec4ec3d09 100644
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -984,6 +984,16 @@
#undef MODULE
/* ======================================================================= */
+ /* 42: NS_ERROR_MODULE_DOM_USER_TIMING */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_USER_TIMING
+ ERROR(NS_ERROR_DOM_UT_UNKNOWN_MARK_NAME, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_UT_INVALID_TIMING_ATTR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_UT_UNAVAILABLE_ATTR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_UT_UNAVAILABLE_GLOBAL_OBJECT, FAILURE(4)),
+#undef MODULE
+
+ /* ======================================================================= */
/* 51: NS_ERROR_MODULE_GENERAL */
/* ======================================================================= */
#define MODULE NS_ERROR_MODULE_GENERAL
diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h
index 3743ecfbed..82d368e107 100644
--- a/xpcom/base/nsError.h
+++ b/xpcom/base/nsError.h
@@ -79,6 +79,7 @@
#define NS_ERROR_MODULE_DOM_ANIM 39
#define NS_ERROR_MODULE_DOM_PUSH 40
#define NS_ERROR_MODULE_DOM_MEDIA 41
+#define NS_ERROR_MODULE_DOM_USER_TIMING 42
/* NS_ERROR_MODULE_GENERAL should be used by modules that do not
* care if return code values overlap. Callers of methods that