mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Issue #2053 - Part 2: Update PerformanceMeasure to User Timing L3
There are a few minor differences between this and Mozilla's implementation: (a) The type error messages were not hardcoded to the Performance class and were moved to Errors.msg instead. (b) PerformanceMeasureOptions is used directly and doesn't use the the Maybe<> container class. I haven't found the reason yet, but PerformanceMeasureOptions is disallowed from having a copy constructor and without that, it isn't possible to use it with Maybe<>... There doesn't seem to be any problem with using it directly, though. (c) Resist fingerprinting-pref changes were skipped. Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=1762482
This commit is contained in:
@@ -100,3 +100,8 @@ 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_UNKNOWN_MARK_NAME, 1, JSEXN_SYNTAXERR, "Given mark name, {0}, is unknown.")
|
||||
|
||||
+181
-32
@@ -20,6 +20,7 @@
|
||||
#include "mozilla/dom/PerformanceNavigationBinding.h"
|
||||
#include "mozilla/dom/PerformanceObserverBinding.h"
|
||||
#include "mozilla/dom/PerformanceNavigationTiming.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TimerClamping.h"
|
||||
@@ -65,6 +66,12 @@ private:
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
enum class Performance::ResolveTimestampAttribute {
|
||||
Start,
|
||||
End,
|
||||
Duration,
|
||||
};
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Performance)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
@@ -257,8 +264,8 @@ Performance::ClearMarks(const Optional<nsAString>& aName)
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::ResolveTimestampFromName(const nsAString& aName,
|
||||
ErrorResult& aRv)
|
||||
Performance::ConvertMarkToTimestampWithString(const nsAString& aName,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
|
||||
DOMHighResTimeStamp ts;
|
||||
@@ -272,7 +279,7 @@ Performance::ResolveTimestampFromName(const nsAString& aName,
|
||||
}
|
||||
|
||||
if (!IsPerformanceTimingAttribute(aName)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
aRv.ThrowTypeError<MSG_PMO_UNKNOWN_MARK_NAME>(aName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -285,50 +292,192 @@ Performance::ResolveTimestampFromName(const nsAString& aName,
|
||||
return ts - CreationTime();
|
||||
}
|
||||
|
||||
void
|
||||
Performance::Measure(const nsAString& aName,
|
||||
const Optional<nsAString>& aStartMark,
|
||||
DOMHighResTimeStamp
|
||||
Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp(
|
||||
const ResolveTimestampAttribute aAttribute,
|
||||
const DOMHighResTimeStamp aTimestamp,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aTimestamp < 0) {
|
||||
nsAutoString attributeName;
|
||||
switch (aAttribute) {
|
||||
case ResolveTimestampAttribute::Start:
|
||||
attributeName = NS_LITERAL_STRING("start");
|
||||
break;
|
||||
case ResolveTimestampAttribute::End:
|
||||
attributeName = NS_LITERAL_STRING("end");
|
||||
break;
|
||||
case ResolveTimestampAttribute::Duration:
|
||||
attributeName = NS_LITERAL_STRING("duration");
|
||||
break;
|
||||
}
|
||||
|
||||
aRv.ThrowTypeError<MSG_NO_NEGATIVE_ATTR>(attributeName);
|
||||
}
|
||||
return aTimestamp;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::ConvertMarkToTimestamp(
|
||||
const ResolveTimestampAttribute aAttribute,
|
||||
const OwningStringOrDouble& aMarkNameOrTimestamp,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aMarkNameOrTimestamp.IsString()) {
|
||||
return ConvertMarkToTimestampWithString(aMarkNameOrTimestamp.GetAsString(),
|
||||
aRv);
|
||||
}
|
||||
return ConvertMarkToTimestampWithDOMHighResTimeStamp(
|
||||
aAttribute, aMarkNameOrTimestamp.GetAsDouble(), aRv);
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::ResolveEndTimeForMeasure(
|
||||
const Optional<nsAString>& aEndMark,
|
||||
const PerformanceMeasureOptions* aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
DOMHighResTimeStamp endTime;
|
||||
if (aEndMark.WasPassed()) {
|
||||
endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv);
|
||||
} else if (aOptions != nullptr && aOptions->mEnd.WasPassed()) {
|
||||
endTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::End,
|
||||
aOptions->mEnd.Value(), aRv);
|
||||
} else if (aOptions != nullptr && aOptions->mStart.WasPassed() &&
|
||||
aOptions->mDuration.WasPassed()) {
|
||||
const DOMHighResTimeStamp start = ConvertMarkToTimestamp(
|
||||
ResolveTimestampAttribute::Start, aOptions->mStart.Value(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return 0;
|
||||
}
|
||||
const DOMHighResTimeStamp duration =
|
||||
ConvertMarkToTimestampWithDOMHighResTimeStamp(
|
||||
ResolveTimestampAttribute::Duration,
|
||||
aOptions->mDuration.Value(),
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
endTime = start + duration;
|
||||
} else {
|
||||
endTime = Now();
|
||||
}
|
||||
return endTime;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::ResolveStartTimeForMeasure(
|
||||
const nsAString* aStartMark,
|
||||
const PerformanceMeasureOptions* aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
DOMHighResTimeStamp startTime;
|
||||
if (aOptions != nullptr && aOptions->mStart.WasPassed()) {
|
||||
startTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::Start,
|
||||
aOptions->mStart.Value(),
|
||||
aRv);
|
||||
} else if (aOptions != nullptr && aOptions->mDuration.WasPassed() &&
|
||||
aOptions->mEnd.WasPassed()) {
|
||||
const DOMHighResTimeStamp duration =
|
||||
ConvertMarkToTimestampWithDOMHighResTimeStamp(
|
||||
ResolveTimestampAttribute::Duration,
|
||||
aOptions->mDuration.Value(),
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const DOMHighResTimeStamp end = ConvertMarkToTimestamp(
|
||||
ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
startTime = end - duration;
|
||||
} else if (aStartMark != nullptr) {
|
||||
startTime = ConvertMarkToTimestampWithString(*aStartMark, aRv);
|
||||
} else {
|
||||
startTime = 0;
|
||||
}
|
||||
|
||||
return startTime;
|
||||
}
|
||||
|
||||
already_AddRefed<PerformanceMeasure>
|
||||
Performance::Measure(JSContext* aCx,
|
||||
const nsAString& aName,
|
||||
const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions,
|
||||
const Optional<nsAString>& aEndMark,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Don't add the entry if the buffer is full. XXX should be removed by bug
|
||||
// 1159003.
|
||||
if (mUserEntries.Length() >= mResourceTimingBufferSize) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp startTime;
|
||||
DOMHighResTimeStamp endTime;
|
||||
|
||||
if (IsPerformanceTimingAttribute(aName)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
const PerformanceMeasureOptions* options = nullptr;
|
||||
if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) {
|
||||
options = &aStartOrMeasureOptions.GetAsPerformanceMeasureOptions();
|
||||
}
|
||||
|
||||
if (aStartMark.WasPassed()) {
|
||||
startTime = ResolveTimestampFromName(aStartMark.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
const bool isOptionsNotEmpty =
|
||||
(options != nullptr) &&
|
||||
(!options->mDetail.isUndefined() || options->mStart.WasPassed() ||
|
||||
options->mEnd.WasPassed() || options->mDuration.WasPassed());
|
||||
if (isOptionsNotEmpty) {
|
||||
if (aEndMark.WasPassed()) {
|
||||
aRv.ThrowTypeError<MSG_PMO_NO_SEPARATE_ENDMARK>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) {
|
||||
aRv.ThrowTypeError<MSG_PMO_MISSING_STARTENDMARK>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (options->mStart.WasPassed() && options->mDuration.WasPassed() &&
|
||||
options->mEnd.WasPassed()) {
|
||||
aRv.ThrowTypeError<MSG_PMO_INVALID_MEMBERS>();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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;
|
||||
detail.setNull();
|
||||
}
|
||||
|
||||
if (aEndMark.WasPassed()) {
|
||||
endTime = ResolveTimestampFromName(aEndMark.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
endTime = Now();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -20,11 +20,16 @@ class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class OwningStringOrDouble;
|
||||
class StringOrPerformanceMeasureOptions;
|
||||
class PerformanceEntry;
|
||||
struct PerformanceMeasureOptions;
|
||||
class PerformanceMeasure;
|
||||
class PerformanceNavigation;
|
||||
class PerformanceObserver;
|
||||
class PerformanceService;
|
||||
class PerformanceTiming;
|
||||
struct StructuredSerializeOptions;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
@@ -75,10 +80,10 @@ public:
|
||||
|
||||
void ClearMarks(const Optional<nsAString>& aName);
|
||||
|
||||
void Measure(const nsAString& aName,
|
||||
const Optional<nsAString>& aStartMark,
|
||||
const Optional<nsAString>& aEndMark,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<PerformanceMeasure> Measure(
|
||||
JSContext* aCx, const nsAString& aName,
|
||||
const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions,
|
||||
const Optional<nsAString>& aEndMark, ErrorResult& aRv);
|
||||
|
||||
void ClearMeasures(const Optional<nsAString>& aName);
|
||||
|
||||
@@ -116,9 +121,6 @@ protected:
|
||||
void ClearUserEntries(const Optional<nsAString>& aEntryName,
|
||||
const nsAString& aEntryType);
|
||||
|
||||
DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual nsISupports* GetAsISupports() = 0;
|
||||
|
||||
virtual void DispatchBufferFullEvent() = 0;
|
||||
@@ -163,6 +165,29 @@ protected:
|
||||
bool mPendingNotificationObserversTask;
|
||||
|
||||
RefPtr<PerformanceService> mPerformanceService;
|
||||
|
||||
private:
|
||||
// The attributes of a PerformanceMeasureOptions that we call
|
||||
// ResolveTimestamp* on.
|
||||
enum class ResolveTimestampAttribute;
|
||||
|
||||
DOMHighResTimeStamp ConvertMarkToTimestampWithString(const nsAString& aName,
|
||||
ErrorResult& aRv);
|
||||
DOMHighResTimeStamp ConvertMarkToTimestampWithDOMHighResTimeStamp(
|
||||
const ResolveTimestampAttribute aAttribute, const double aTimestamp,
|
||||
ErrorResult& aRv);
|
||||
DOMHighResTimeStamp ConvertMarkToTimestamp(
|
||||
const ResolveTimestampAttribute aAttribute,
|
||||
const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv);
|
||||
|
||||
DOMHighResTimeStamp ResolveEndTimeForMeasure(
|
||||
const Optional<nsAString>& aEndMark,
|
||||
const PerformanceMeasureOptions* aOptions,
|
||||
ErrorResult& aRv);
|
||||
DOMHighResTimeStamp ResolveStartTimeForMeasure(
|
||||
const nsAString* aStartMark,
|
||||
const PerformanceMeasureOptions* aOptions,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
@@ -72,7 +80,9 @@ partial interface Performance {
|
||||
[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);
|
||||
};
|
||||
|
||||
@@ -10,4 +10,5 @@
|
||||
[Exposed=(Window,Worker)]
|
||||
interface PerformanceMeasure : PerformanceEntry
|
||||
{
|
||||
readonly attribute any detail;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user