Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-04-12 10:05:03 +08:00
commit 265490b550
18 changed files with 554 additions and 95 deletions

View file

@ -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, "The URI is malformed.", NS_ERROR_DOM_MALFORMED_URI)
DOM4_MSG_DEF(SyntaxError, "Invalid header name.", NS_ERROR_DOM_INVALID_HEADER_NAME) 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. */ /* 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 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) DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must be OPENED.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED)

View file

@ -659,6 +659,12 @@ DOMInterfaces = {
'wrapperCache': False 'wrapperCache': False
}, },
'Performance' : {
'implicitJSContext': [
'mark'
],
},
'Plugin': { 'Plugin': {
'headerFile' : 'nsPluginArray.h', 'headerFile' : 'nsPluginArray.h',
'nativeType': 'nsPluginElement', 'nativeType': 'nsPluginElement',

View file

@ -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_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_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_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}.")

View file

@ -195,6 +195,7 @@ CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
case NS_ERROR_MODULE_DOM_ANIM: case NS_ERROR_MODULE_DOM_ANIM:
case NS_ERROR_MODULE_DOM_PUSH: case NS_ERROR_MODULE_DOM_PUSH:
case NS_ERROR_MODULE_DOM_MEDIA: case NS_ERROR_MODULE_DOM_MEDIA:
case NS_ERROR_MODULE_DOM_USER_TIMING:
if (aMessage.IsEmpty()) { if (aMessage.IsEmpty()) {
return DOMException::Create(aRv); return DOMException::Create(aRv);
} }

View file

@ -20,11 +20,13 @@
#include "mozilla/dom/PerformanceNavigationBinding.h" #include "mozilla/dom/PerformanceNavigationBinding.h"
#include "mozilla/dom/PerformanceObserverBinding.h" #include "mozilla/dom/PerformanceObserverBinding.h"
#include "mozilla/dom/PerformanceNavigationTiming.h" #include "mozilla/dom/PerformanceNavigationTiming.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/TimerClamping.h" #include "mozilla/TimerClamping.h"
#include "WorkerPrivate.h" #include "WorkerPrivate.h"
#include "WorkerRunnable.h" #include "WorkerRunnable.h"
#include "WorkerScope.h"
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__) #define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
@ -65,6 +67,12 @@ private:
} // anonymous namespace } // anonymous namespace
enum class Performance::ResolveTimestampAttribute {
Start,
End,
Duration,
};
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Performance) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Performance)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
@ -98,6 +106,27 @@ Performance::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate)
return performance.forget(); 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() Performance::Performance()
: mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) : mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
, mPendingNotificationObserversTask(false) , mPendingNotificationObserversTask(false)
@ -227,27 +256,42 @@ Performance::RoundTime(double aTime) const
return floor(aTime / maxResolutionMs) * maxResolutionMs; return floor(aTime / maxResolutionMs) * maxResolutionMs;
} }
already_AddRefed<PerformanceMark> Performance::Mark(
void JSContext* aCx,
Performance::Mark(const nsAString& aName, ErrorResult& aRv) 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. // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
if (mUserEntries.Length() >= mResourceTimingBufferSize) { if (mUserEntries.Length() >= mResourceTimingBufferSize) {
return; return nullptr;
} }
if (IsPerformanceTimingAttribute(aName)) { nsCOMPtr<nsIGlobalObject> parent = GetParentObject();
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); if (!parent || parent->IsDying() || !parent->GetGlobalJSObject()) {
return; 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 = RefPtr<PerformanceMark> performanceMark =
new PerformanceMark(GetAsISupports(), aName, Now()); PerformanceMark::Constructor(global, aName, aMarkOptions, aRv);
if (aRv.Failed()) {
return nullptr;
}
InsertUserEntry(performanceMark); InsertUserEntry(performanceMark);
if (profiler_is_active()) { if (profiler_is_active()) {
PROFILER_MARKER(NS_ConvertUTF16toUTF8(aName).get()); PROFILER_MARKER(NS_ConvertUTF16toUTF8(aName).get());
} }
return performanceMark.forget();
} }
void void
@ -256,12 +300,37 @@ Performance::ClearMarks(const Optional<nsAString>& aName)
ClearUserEntries(aName, NS_LITERAL_STRING("mark")); ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
} }
DOMHighResTimeStamp // To be removed once bug 1124165 lands
Performance::ResolveTimestampFromName(const nsAString& aName, bool
ErrorResult& aRv) 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::ConvertMarkToTimestampWithString(const nsAString& aName,
ErrorResult& aRv)
{
if (IsPerformanceTimingAttribute(aName)) {
return ConvertNameToTimestamp(aName, aRv);
}
AutoTArray<RefPtr<PerformanceEntry>, 1> arr; AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
DOMHighResTimeStamp ts;
Optional<nsAString> typeParam; Optional<nsAString> typeParam;
nsAutoString str; nsAutoString str;
str.AssignLiteral("mark"); str.AssignLiteral("mark");
@ -271,64 +340,223 @@ Performance::ResolveTimestampFromName(const nsAString& aName,
return arr.LastElement()->StartTime(); return arr.LastElement()->StartTime();
} }
if (!IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_UT_UNKNOWN_MARK_NAME);
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return 0;
return 0;
}
ts = GetPerformanceTimingFromString(aName);
if (!ts) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return 0;
}
return ts - CreationTime();
} }
void DOMHighResTimeStamp
Performance::Measure(const nsAString& aName, Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp(
const Optional<nsAString>& aStartMark, 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;
}
if (aName.EqualsASCII("navigationStart")) {
return 0;
}
// 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;
}
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, const Optional<nsAString>& aEndMark,
ErrorResult& aRv) ErrorResult& aRv)
{ {
// Don't add the entry if the buffer is full. XXX should be removed by bug // Don't add the entry if the buffer is full. XXX should be removed by bug
// 1159003. // 1159003.
if (mUserEntries.Length() >= mResourceTimingBufferSize) { if (mUserEntries.Length() >= mResourceTimingBufferSize) {
return; return nullptr;
} }
DOMHighResTimeStamp startTime; const PerformanceMeasureOptions* options = nullptr;
DOMHighResTimeStamp endTime; if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) {
options = &aStartOrMeasureOptions.GetAsPerformanceMeasureOptions();
if (IsPerformanceTimingAttribute(aName)) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
} }
if (aStartMark.WasPassed()) { const bool isOptionsNotEmpty =
startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); (options != nullptr) &&
if (NS_WARN_IF(aRv.Failed())) { (!options->mDetail.isUndefined() || options->mStart.WasPassed() ||
return; 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;
}
}
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 { } else {
// Navigation start is used in this case, but since DOMHighResTimeStamp is detail.setNull();
// in relation to navigation start, this will be zero if a name is not
// passed.
startTime = 0;
} }
if (aEndMark.WasPassed()) { RefPtr<PerformanceMeasure> performanceMeasure = new PerformanceMeasure(
endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); GetAsISupports(), aName, startTime, endTime, detail);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
} else {
endTime = Now();
}
RefPtr<PerformanceMeasure> performanceMeasure =
new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime);
InsertUserEntry(performanceMeasure); InsertUserEntry(performanceMeasure);
return performanceMeasure.forget();
} }
void void

View file

@ -20,11 +20,18 @@ class ErrorResult;
namespace dom { namespace dom {
class OwningStringOrDouble;
class StringOrPerformanceMeasureOptions;
class PerformanceEntry; class PerformanceEntry;
class PerformanceMark;
struct PerformanceMarkOptions;
struct PerformanceMeasureOptions;
class PerformanceMeasure;
class PerformanceNavigation; class PerformanceNavigation;
class PerformanceObserver; class PerformanceObserver;
class PerformanceService; class PerformanceService;
class PerformanceTiming; class PerformanceTiming;
struct StructuredSerializeOptions;
namespace workers { namespace workers {
class WorkerPrivate; class WorkerPrivate;
@ -50,6 +57,10 @@ public:
static already_AddRefed<Performance> static already_AddRefed<Performance>
CreateForWorker(workers::WorkerPrivate* aWorkerPrivate); 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, JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> aGivenProto) override; JS::Handle<JSObject*> aGivenProto) override;
@ -71,14 +82,16 @@ public:
DOMHighResTimeStamp TimeOrigin(); 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 ClearMarks(const Optional<nsAString>& aName);
void Measure(const nsAString& aName, already_AddRefed<PerformanceMeasure> Measure(
const Optional<nsAString>& aStartMark, JSContext* aCx, const nsAString& aName,
const Optional<nsAString>& aEndMark, const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions,
ErrorResult& aRv); const Optional<nsAString>& aEndMark, ErrorResult& aRv);
void ClearMeasures(const Optional<nsAString>& aName); void ClearMeasures(const Optional<nsAString>& aName);
@ -104,6 +117,13 @@ public:
virtual nsITimedChannel* GetChannel() const = 0; virtual nsITimedChannel* GetChannel() const = 0;
bool IsPerformanceTimingAttribute(const nsAString& aName) const;
virtual bool IsGlobalObjectWindow() const
{
return false;
}
protected: protected:
Performance(); Performance();
explicit Performance(nsPIDOMWindowInner* aWindow); explicit Performance(nsPIDOMWindowInner* aWindow);
@ -116,9 +136,6 @@ protected:
void ClearUserEntries(const Optional<nsAString>& aEntryName, void ClearUserEntries(const Optional<nsAString>& aEntryName,
const nsAString& aEntryType); const nsAString& aEntryType);
DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName,
ErrorResult& aRv);
virtual nsISupports* GetAsISupports() = 0; virtual nsISupports* GetAsISupports() = 0;
virtual void DispatchBufferFullEvent() = 0; virtual void DispatchBufferFullEvent() = 0;
@ -127,11 +144,6 @@ protected:
virtual DOMHighResTimeStamp CreationTime() const = 0; virtual DOMHighResTimeStamp CreationTime() const = 0;
virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
{
return false;
}
virtual DOMHighResTimeStamp virtual DOMHighResTimeStamp
GetPerformanceTimingFromString(const nsAString& aTimingName) GetPerformanceTimingFromString(const nsAString& aTimingName)
{ {
@ -159,10 +171,36 @@ protected:
nsTArray<RefPtr<PerformanceEntry>> mResourceEntries; nsTArray<RefPtr<PerformanceEntry>> mResourceEntries;
uint64_t mResourceTimingBufferSize; uint64_t mResourceTimingBufferSize;
static const uint64_t kDefaultResourceTimingBufferSize = 150; static const uint64_t kDefaultResourceTimingBufferSize = 1500;
bool mPendingNotificationObserversTask; bool mPendingNotificationObserversTask;
RefPtr<PerformanceService> mPerformanceService; 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 } // namespace dom

View file

@ -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 DOMHighResTimeStamp
PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty) PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
{ {

View file

@ -57,6 +57,11 @@ public:
const Optional<nsAString>& aEntryType, const Optional<nsAString>& aEntryType,
nsTArray<RefPtr<PerformanceEntry>>& aRetval) override; nsTArray<RefPtr<PerformanceEntry>>& aRetval) override;
bool IsGlobalObjectWindow() const override
{
return true;
}
protected: protected:
~PerformanceMainThread(); ~PerformanceMainThread();
@ -67,8 +72,6 @@ protected:
void InsertUserEntry(PerformanceEntry* aEntry) override; void InsertUserEntry(PerformanceEntry* aEntry) override;
bool IsPerformanceTimingAttribute(const nsAString& aName) override;
DOMHighResTimeStamp DOMHighResTimeStamp
GetPerformanceTimingFromString(const nsAString& aTimingName) override; GetPerformanceTimingFromString(const nsAString& aTimingName) override;

View file

@ -6,25 +6,119 @@
#include "PerformanceMark.h" #include "PerformanceMark.h"
#include "MainThreadUtils.h" #include "MainThreadUtils.h"
#include "mozilla/dom/PerformanceMarkBinding.h" #include "mozilla/dom/PerformanceMarkBinding.h"
#include "mozilla/dom/MessagePortBinding.h"
using mozilla::dom::StructuredSerializeOptions;
using namespace mozilla::dom; using namespace mozilla::dom;
PerformanceMark::PerformanceMark(nsISupports* aParent, PerformanceMark::PerformanceMark(nsISupports* aParent,
const nsAString& aName, const nsAString& aName,
DOMHighResTimeStamp aStartTime) DOMHighResTimeStamp aStartTime,
const JS::Handle<JS::Value>& aDetail)
: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("mark")) : PerformanceEntry(aParent, aName, NS_LITERAL_STRING("mark"))
, mStartTime(aStartTime) , mStartTime(aStartTime)
, mDetail(aDetail)
{ {
// mParent is null in workers. // mParent is null in workers.
MOZ_ASSERT(mParent || !NS_IsMainThread()); 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() 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* JSObject*
PerformanceMark::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) PerformanceMark::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{ {
return PerformanceMarkBinding::Wrap(aCx, this, 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);
}

View file

@ -11,16 +11,40 @@
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
struct PerformanceMarkOptions;
// http://www.w3.org/TR/user-timing/#performancemark // http://www.w3.org/TR/user-timing/#performancemark
class PerformanceMark final : public PerformanceEntry class PerformanceMark final : public PerformanceEntry
{ {
public: private:
PerformanceMark(nsISupports* aParent, PerformanceMark(nsISupports* aParent,
const nsAString& aName, 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; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRv);
virtual DOMHighResTimeStamp StartTime() const override virtual DOMHighResTimeStamp StartTime() const override
{ {
return mStartTime; return mStartTime;

View file

@ -12,10 +12,12 @@ using namespace mozilla::dom;
PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, PerformanceMeasure::PerformanceMeasure(nsISupports* aParent,
const nsAString& aName, const nsAString& aName,
DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aStartTime,
DOMHighResTimeStamp aEndTime) DOMHighResTimeStamp aEndTime,
: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure")), const JS::Handle<JS::Value>& aDetail)
mStartTime(aStartTime), : PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure"))
mDuration(aEndTime - aStartTime) , mStartTime(aStartTime)
, mDuration(aEndTime - aStartTime)
, mDetail(aDetail)
{ {
// mParent is null in workers. // mParent is null in workers.
MOZ_ASSERT(mParent || !NS_IsMainThread()); MOZ_ASSERT(mParent || !NS_IsMainThread());
@ -23,10 +25,43 @@ PerformanceMeasure::PerformanceMeasure(nsISupports* aParent,
PerformanceMeasure::~PerformanceMeasure() 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* JSObject*
PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) PerformanceMeasure::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{ {
return PerformanceMeasureBinding::Wrap(aCx, this, 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);
}

View file

@ -15,10 +15,15 @@ namespace dom {
class PerformanceMeasure final : public PerformanceEntry class PerformanceMeasure final : public PerformanceEntry
{ {
public: public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMeasure,
PerformanceEntry);
PerformanceMeasure(nsISupports* aParent, PerformanceMeasure(nsISupports* aParent,
const nsAString& aName, const nsAString& aName,
DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aStartTime,
DOMHighResTimeStamp aEndTime); DOMHighResTimeStamp aEndTime,
const JS::Handle<JS::Value>& aDetail);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@ -32,10 +37,15 @@ public:
return mDuration; return mDuration;
} }
void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRv);
protected: protected:
virtual ~PerformanceMeasure(); virtual ~PerformanceMeasure();
DOMHighResTimeStamp mStartTime; DOMHighResTimeStamp mStartTime;
DOMHighResTimeStamp mDuration; DOMHighResTimeStamp mDuration;
private:
JS::Heap<JS::Value> mDetail;
}; };
} // namespace dom } // namespace dom

View file

@ -14,7 +14,7 @@ typedef double DOMHighResTimeStamp;
typedef sequence <PerformanceEntry> PerformanceEntryList; typedef sequence <PerformanceEntry> PerformanceEntryList;
[Exposed=(Window,Worker)] [Exposed=(Window,Worker)]
interface Performance { interface Performance : EventTarget {
[DependsOn=DeviceState, Affects=Nothing] [DependsOn=DeviceState, Affects=Nothing]
DOMHighResTimeStamp now(); DOMHighResTimeStamp now();
@ -44,6 +44,14 @@ partial interface Performance {
entryType); 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 // http://www.w3.org/TR/resource-timing/#extensions-performance-interface
[Exposed=Window] [Exposed=Window]
partial interface Performance { partial interface Performance {
@ -64,15 +72,23 @@ partial interface Performance {
}; };
#endif #endif
// https://w3c.github.io/user-timing/#extensions-performance-interface
dictionary PerformanceMarkOptions {
any detail;
DOMHighResTimeStamp startTime;
};
// http://www.w3.org/TR/user-timing/ // http://www.w3.org/TR/user-timing/
[Exposed=(Window,Worker)] [Exposed=(Window,Worker)]
partial interface Performance { partial interface Performance {
[Func="Performance::IsEnabled", Throws] [Func="Performance::IsEnabled", Throws]
void mark(DOMString markName); PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions);
[Func="Performance::IsEnabled"] [Func="Performance::IsEnabled"]
void clearMarks(optional DOMString markName); void clearMarks(optional DOMString markName);
[Func="Performance::IsEnabled", Throws] [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"] [Func="Performance::IsEnabled"]
void clearMeasures(optional DOMString measureName); void clearMeasures(optional DOMString measureName);
}; };

View file

@ -7,7 +7,8 @@
* http://www.w3.org/TR/user-timing/#performancemark * http://www.w3.org/TR/user-timing/#performancemark
*/ */
[Exposed=(Window,Worker)] [Exposed=(Window,Worker), Constructor(DOMString markName, optional PerformanceMarkOptions markOptions)]
interface PerformanceMark : PerformanceEntry interface PerformanceMark : PerformanceEntry
{ {
readonly attribute any detail;
}; };

View file

@ -10,4 +10,5 @@
[Exposed=(Window,Worker)] [Exposed=(Window,Worker)]
interface PerformanceMeasure : PerformanceEntry interface PerformanceMeasure : PerformanceEntry
{ {
readonly attribute any detail;
}; };

View file

@ -189,7 +189,7 @@ pref("dom.enable_resource_timing", true);
pref("dom.enable_user_timing", true); pref("dom.enable_user_timing", true);
// Whether performance.GetEntries* will contain an entry for the active document // Whether performance.GetEntries* will contain an entry for the active document
pref("dom.enable_performance_navigation_timing", false); pref("dom.enable_performance_navigation_timing", true);
// Enable printing performance marks/measures to log // Enable printing performance marks/measures to log
pref("dom.performance.enable_user_timing_logging", false); pref("dom.performance.enable_user_timing_logging", false);

View file

@ -990,6 +990,16 @@
ERROR(NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR, FAILURE(101)), ERROR(NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR, FAILURE(101)),
#undef MODULE #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 */ /* 51: NS_ERROR_MODULE_GENERAL */
/* ======================================================================= */ /* ======================================================================= */

View file

@ -79,6 +79,7 @@
#define NS_ERROR_MODULE_DOM_ANIM 39 #define NS_ERROR_MODULE_DOM_ANIM 39
#define NS_ERROR_MODULE_DOM_PUSH 40 #define NS_ERROR_MODULE_DOM_PUSH 40
#define NS_ERROR_MODULE_DOM_MEDIA 41 #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 /* NS_ERROR_MODULE_GENERAL should be used by modules that do not
* care if return code values overlap. Callers of methods that * care if return code values overlap. Callers of methods that