Issue #2053 - Part 3: Update PerformanceMark to User Timing L3

There are a few minor differences between this and Mozilla's implementation:
(a) The type error messages were not hardcoded to the Performance class and were moved to Errors.msg or domerr.msg instead.
(b) Resist fingerprinting-pref changes were skipped.

Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=1724645
This commit is contained in:
FranklinDM 2023-04-06 19:32:32 +08:00 committed by roytam1
commit 3b57ba1415
8 changed files with 197 additions and 19 deletions

View file

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

View file

@ -105,3 +105,6 @@ MSG_DEF(MSG_PMO_NO_SEPARATE_ENDMARK, 0, JSEXN_TYPEERR, "Cannot provide separate
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_UNKNOWN_MARK_NAME, 1, JSEXN_SYNTAXERR, "Given mark name, {0}, is unknown.")
MSG_DEF(MSG_PMO_CONSTRUCTOR_INACCESSIBLE, 0, JSEXN_TYPEERR, "Can't access PerformanceMark constructor, performance is null.")
MSG_DEF(MSG_PMO_INVALID_TIMING_ATTR, 0, JSEXN_SYNTAXERR, "markName cannot be a performance timing attribute.")
MSG_DEF(MSG_PMO_UNEXPECTED_START_TIME, 0, JSEXN_TYPEERR, "Expected startTime >= 0.")

View file

@ -26,6 +26,7 @@
#include "mozilla/TimerClamping.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
@ -105,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)
@ -234,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_INVALID_STATE_ERR);
return nullptr;
}
GlobalObject global(aCx, parent->GetGlobalJSObject());
if (global.Failed()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
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

View file

@ -23,6 +23,8 @@ namespace dom {
class OwningStringOrDouble;
class StringOrPerformanceMeasureOptions;
class PerformanceEntry;
class PerformanceMark;
struct PerformanceMarkOptions;
struct PerformanceMeasureOptions;
class PerformanceMeasure;
class PerformanceNavigation;
@ -55,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;
@ -76,7 +82,9 @@ 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);
@ -109,6 +117,11 @@ public:
virtual nsITimedChannel* GetChannel() const = 0;
virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
{
return false;
}
protected:
Performance();
explicit Performance(nsPIDOMWindowInner* aWindow);
@ -129,11 +142,6 @@ protected:
virtual DOMHighResTimeStamp CreationTime() const = 0;
virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
{
return false;
}
virtual DOMHighResTimeStamp
GetPerformanceTimingFromString(const nsAString& aTimingName)
{

View file

@ -6,25 +6,118 @@
#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->IsPerformanceTimingAttribute(aMarkName)) {
aRv.ThrowTypeError<MSG_PMO_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);
}

View file

@ -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;

View file

@ -72,11 +72,17 @@ 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]

View file

@ -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;
};