Commit 95867883 authored by Liquan(Max) Gu's avatar Liquan(Max) Gu Committed by Commit Bot

[UserTimingL3] Fix meature, mark API feature detection

UserTimingL3 uses returned value for feature detection. If measure and mark
API returns null, the APIs are L2 API; If the APIs return an entry, the APIs are
L3 API. However, in the current implementation, when L3 API is enabled, the
APIs do not always return the created entry.

This CL is to fix this bug, and add layout tests to verify it.

Change-Id: I9854f0abd0d64a3334701e09d6ce0fc245fcca3e
Reviewed-on: https://chromium-review.googlesource.com/c/1338225
Commit-Queue: Liquan (Max) Gǔ <maxlg@chromium.org>
Reviewed-by: default avatarNicolás Peña Moreno <npm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608552}
parent fc0cd996
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>User Timing: L2 APIs return null</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<p>User Timing: L2 APIs return null</p>
<div id="log"></div>
<script>
async_test(function (t) {
self.performance.clearMeasures();
const measure = self.performance.measure("measure1");
assert_equals(measure, null);
t.done();
}, "L2: performance.measure(name) should return null.");
async_test(function (t) {
self.performance.clearMeasures();
self.performance.mark("1");
const measure = self.performance.measure("measure2", 1);
assert_equals(measure, null);
t.done();
}, "L2: performance.measure(name, param1) should return null.");
async_test(function (t) {
self.performance.clearMeasures();
self.performance.mark("1");
self.performance.mark("2");
const measure = self.performance.measure("measure3", 1, 2);
assert_equals(measure, null);
t.done();
}, "L2: performance.measure(name, param1, param2) should return null.");
async_test(function (t) {
self.performance.clearMarks();
const mark = self.performance.mark("mark1");
assert_equals(mark, null);
t.done();
}, "L2: performance.mark(name) should return null.");
async_test(function (t) {
self.performance.clearMarks();
const mark = self.performance.mark("mark2", { startTime: 34 });
assert_equals(mark, null);
t.done();
}, "L2: performance.mark(name, param) should return null.");
</script>
...@@ -3,22 +3,42 @@ ...@@ -3,22 +3,42 @@
<title>Custom User Timing: L3 API returns a mark/measure object</title> <title>Custom User Timing: L3 API returns a mark/measure object</title>
<script src="../../../../resources/testharness.js"></script> <script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script> <script src="../../../../resources/testharnessreport.js"></script>
<script src="performanceobservers.js"></script>
<p>Custom User Timing: L3 API returns a mark/measure object</p> <p>Custom User Timing: L3 API returns a mark/measure object</p>
<div id="log"></div> <div id="log"></div>
<script> <script>
async_test(function (t) { async_test(function (t) {
self.performance.clearMeasures(); self.performance.clearMeasures();
const measure = self.performance.measure("measure1", const measure = self.performance.measure("measure1");
assert_true(measure instanceof PerformanceMeasure);
t.done();
}, "L3: performance.measure(name) should return an entry.");
async_test(function (t) {
self.performance.clearMeasures();
const measure = self.performance.measure("measure2",
{ startTime: 12, endTime:23 }); { startTime: 12, endTime:23 });
assert_equals(measure.startTime, 12); assert_true(measure instanceof PerformanceMeasure);
t.done();
}, "L3: performance.measure(name, param1) should return an entry.");
async_test(function (t) {
self.performance.clearMeasures();
const measure = self.performance.measure("measure3", 1, 2);
assert_true(measure instanceof PerformanceMeasure);
t.done();
}, "L3: performance.measure(name, param1, param2) should return an entry.");
async_test(function (t) {
self.performance.clearMarks();
const mark = self.performance.mark("mark1");
assert_true(mark instanceof PerformanceMark);
t.done(); t.done();
}, "L3 measure API returns a measure object."); }, "L3: performance.mark(name) should return an entry.");
async_test(function (t) { async_test(function (t) {
self.performance.clearMarks(); self.performance.clearMarks();
const mark = self.performance.mark("mark1", { startTime: 34 }); const mark = self.performance.mark("mark2", { startTime: 34 });
assert_equals(mark.startTime, 34); assert_true(mark instanceof PerformanceMark);
t.done(); t.done();
}, "L3 mark API returns a mark object."); }, "L3: performance.mark(name, param) should return an entry.");
</script> </script>
...@@ -11,24 +11,25 @@ Performance.mark method takes in custom detail and custom start time. ...@@ -11,24 +11,25 @@ Performance.mark method takes in custom detail and custom start time.
<div id="log"></div> <div id="log"></div>
<script> <script>
async_test(function (t) { async_test(function (t) {
var mark_entries = []; let mark_entries = [];
var now = self.performance.now(); const now = self.performance.now();
var observer = new PerformanceObserver( const expected_entries =
[{ entryType: "mark", name: "mark1", detail: null},
{ entryType: "mark", name: "mark2", detail: null},
{ entryType: "mark", name: "mark3", detail: null},
{ entryType: "mark", name: "mark4", detail: null},
{ entryType: "mark", name: "mark5", detail: null},
{ entryType: "mark", name: "mark6", detail: {}},
{ entryType: "mark", name: "mark7", detail: {info: 'abc'}},
{ entryType: "mark", name: "mark8", detail: null, startTime: now},
{ entryType: "mark", name: "mark9", detail: null, startTime: 234.56},
{ entryType: "mark", name: "mark10", detail: {count: 3}, startTime: 345.67}];
const observer = new PerformanceObserver(
t.step_func(function (entryList, obs) { t.step_func(function (entryList, obs) {
mark_entries = mark_entries =
mark_entries.concat(entryList.getEntries()); mark_entries.concat(entryList.getEntries());
if (mark_entries.length >= 10) { if (mark_entries.length >= 10) {
checkEntries(mark_entries, checkEntries(mark_entries, expected_entries);
[{ entryType: "mark", name: "mark1", detail: null},
{ entryType: "mark", name: "mark2", detail: null},
{ entryType: "mark", name: "mark3", detail: null},
{ entryType: "mark", name: "mark4", detail: null},
{ entryType: "mark", name: "mark5", detail: null},
{ entryType: "mark", name: "mark6", detail: {}},
{ entryType: "mark", name: "mark7", detail: {info: 'abc'}},
{ entryType: "mark", name: "mark8", detail: null, startTime: now},
{ entryType: "mark", name: "mark9", detail: null, startTime: 234.56},
{ entryType: "mark", name: "mark10", detail: {count: 3}, startTime: 345.67}]);
observer.disconnect(); observer.disconnect();
t.done(); t.done();
} }
...@@ -36,15 +37,17 @@ Performance.mark method takes in custom detail and custom start time. ...@@ -36,15 +37,17 @@ Performance.mark method takes in custom detail and custom start time.
); );
self.performance.clearMarks(); self.performance.clearMarks();
observer.observe({entryTypes: ["mark"]}); observer.observe({entryTypes: ["mark"]});
self.performance.mark("mark1"); const returned_entries = [];
self.performance.mark("mark2", undefined); returned_entries.push(self.performance.mark("mark1"));
self.performance.mark("mark3", null); returned_entries.push(self.performance.mark("mark2", undefined));
self.performance.mark("mark4", {}); returned_entries.push(self.performance.mark("mark3", null));
self.performance.mark("mark5", {detail: null}); returned_entries.push(self.performance.mark("mark4", {}));
self.performance.mark("mark6", {detail: {}}); returned_entries.push(self.performance.mark("mark5", {detail: null}));
self.performance.mark("mark7", {detail: {info: 'abc'}}); returned_entries.push(self.performance.mark("mark6", {detail: {}}));
self.performance.mark("mark8", now); returned_entries.push(self.performance.mark("mark7", {detail: {info: 'abc'}}));
self.performance.mark("mark9", {startTime: 234.56}); returned_entries.push(self.performance.mark("mark8", now));
self.performance.mark("mark10", {detail: {count: 3}, startTime: 345.67}); returned_entries.push(self.performance.mark("mark9", {startTime: 234.56}));
returned_entries.push(self.performance.mark("mark10", {detail: {count: 3}, startTime: 345.67}));
checkEntries(returned_entries, expected_entries);
}, "mark entries' detail and startTime are customizable"); }, "mark entries' detail and startTime are customizable");
</script> </script>
...@@ -16,7 +16,7 @@ Custom User Timing: measure ...@@ -16,7 +16,7 @@ Custom User Timing: measure
const timeStamp2 = 1234.5; const timeStamp2 = 1234.5;
const timeStamp3 = 66.6; const timeStamp3 = 66.6;
const timeStamp4 = 5566; const timeStamp4 = 5566;
const expectectedEntries = const expectedEntries =
[{ entryType: "measure", name: "measure1", detail: null, startTime: 0 }, [{ entryType: "measure", name: "measure1", detail: null, startTime: 0 },
{ entryType: "measure", name: "measure2", detail: null, startTime: 0 }, { entryType: "measure", name: "measure2", detail: null, startTime: 0 },
{ entryType: "measure", name: "measure3", detail: null, startTime: 0 }, { entryType: "measure", name: "measure3", detail: null, startTime: 0 },
...@@ -46,8 +46,8 @@ Custom User Timing: measure ...@@ -46,8 +46,8 @@ Custom User Timing: measure
t.step_func(function (entryList, obs) { t.step_func(function (entryList, obs) {
measureEntries = measureEntries =
measureEntries.concat(entryList.getEntries()); measureEntries.concat(entryList.getEntries());
if (measureEntries.length >= expectectedEntries.length) { if (measureEntries.length >= expectedEntries.length) {
checkEntries(measureEntries, expectectedEntries); checkEntries(measureEntries, expectedEntries);
observer.disconnect(); observer.disconnect();
t.done(); t.done();
} }
...@@ -59,31 +59,53 @@ Custom User Timing: measure ...@@ -59,31 +59,53 @@ Custom User Timing: measure
self.performance.mark("mark1", { detail: { randomInfo: 3 }, startTime: timeStamp1 }); self.performance.mark("mark1", { detail: { randomInfo: 3 }, startTime: timeStamp1 });
self.performance.mark("mark2", timeStamp2); self.performance.mark("mark2", timeStamp2);
self.performance.measure("measure1"); const returnedEntries = [];
self.performance.measure("measure2", undefined); returnedEntries.push(self.performance.measure("measure1"));
self.performance.measure("measure3", null); returnedEntries.push(self.performance.measure("measure2", undefined));
self.performance.measure("measure4", timeStamp1); returnedEntries.push(self.performance.measure("measure3", null));
self.performance.measure("measure5", 'mark1'); returnedEntries.push(self.performance.measure("measure4", timeStamp1));
self.performance.measure("measure6", undefined, timeStamp1); returnedEntries.push(self.performance.measure("measure5", 'mark1'));
self.performance.measure("measure7", null, 'mark1'); returnedEntries.push(
self.performance.measure("measure8", timeStamp2, null); self.performance.measure("measure6", undefined, timeStamp1));
self.performance.measure("measure9", timeStamp2, timeStamp3); returnedEntries.push(
self.performance.measure("measure10", timeStamp2, 'mark1'); self.performance.measure("measure7", null, 'mark1'));
self.performance.measure("measure11", 'mark1', undefined); returnedEntries.push(
self.performance.measure("measure12", 'mark1', timeStamp3); self.performance.measure("measure8", timeStamp2, null));
self.performance.measure("measure13", 'mark1', 'mark2'); returnedEntries.push(
self.performance.measure("measure14", {}); self.performance.measure("measure9", timeStamp2, timeStamp3));
self.performance.measure("measure15", { startTime: null }); returnedEntries.push(
self.performance.measure("measure16", { startTime: undefined }); self.performance.measure("measure10", timeStamp2, 'mark1'));
self.performance.measure("measure17", { startTime: 'mark1' }); returnedEntries.push(
self.performance.measure("measure18", { startTime: timeStamp3 }); self.performance.measure("measure11", 'mark1', undefined));
self.performance.measure("measure19", { endTime: undefined }); returnedEntries.push(
self.performance.measure("measure20", { endTime: 'mark1' }); self.performance.measure("measure12", 'mark1', timeStamp3));
self.performance.measure("measure21", { startTime: timeStamp3, endTime: 'mark1' }); returnedEntries.push(
self.performance.measure("measure22", { startTime: timeStamp1, endTime: timeStamp2, detail: undefined }); self.performance.measure("measure13", 'mark1', 'mark2'));
self.performance.measure("measure23", { startTime: 'mark1', endTime: undefined, detail: null }); returnedEntries.push(
self.performance.measure("measure24", { startTime: null, endTime: timeStamp1, detail: {} }); self.performance.measure("measure14", {}));
self.performance.measure("measure25", { startTime: timeStamp3, endTime: 'mark2', detail: { customInfo: 159 }}); returnedEntries.push(
self.performance.measure("measure15", { startTime: null }));
returnedEntries.push(
self.performance.measure("measure16", { startTime: undefined }));
returnedEntries.push(
self.performance.measure("measure17", { startTime: 'mark1' }));
returnedEntries.push(
self.performance.measure("measure18", { startTime: timeStamp3 }));
returnedEntries.push(
self.performance.measure("measure19", { endTime: undefined }));
returnedEntries.push(
self.performance.measure("measure20", { endTime: 'mark1' }));
returnedEntries.push(
self.performance.measure("measure21", { startTime: timeStamp3, endTime: 'mark1' }));
returnedEntries.push(
self.performance.measure("measure22", { startTime: timeStamp1, endTime: timeStamp2, detail: undefined }));
returnedEntries.push(
self.performance.measure("measure23", { startTime: 'mark1', endTime: undefined, detail: null }));
returnedEntries.push(
self.performance.measure("measure24", { startTime: null, endTime: timeStamp1, detail: {} }));
returnedEntries.push(
self.performance.measure("measure25", { startTime: timeStamp3, endTime: 'mark2', detail: { customInfo: 159 }}));
checkEntries(returnedEntries, expectedEntries);
}, "measure entries' detail and start/end are customizable"); }, "measure entries' detail and start/end are customizable");
async_test(function (t) { async_test(function (t) {
......
...@@ -639,7 +639,7 @@ PerformanceMark* Performance::mark(ScriptState* script_state, ...@@ -639,7 +639,7 @@ PerformanceMark* Performance::mark(ScriptState* script_state,
const AtomicString& mark_name, const AtomicString& mark_name,
ExceptionState& exception_state) { ExceptionState& exception_state) {
DoubleOrPerformanceMarkOptions startOrOptions; DoubleOrPerformanceMarkOptions startOrOptions;
return this->mark(script_state, mark_name, startOrOptions, exception_state); return mark(script_state, mark_name, startOrOptions, exception_state);
} }
PerformanceMark* Performance::mark( PerformanceMark* Performance::mark(
...@@ -676,7 +676,9 @@ PerformanceMark* Performance::mark( ...@@ -676,7 +676,9 @@ PerformanceMark* Performance::mark(
script_state, mark_name, start, detail, exception_state); script_state, mark_name, start, detail, exception_state);
if (performance_mark) if (performance_mark)
NotifyObserversOfEntry(*performance_mark); NotifyObserversOfEntry(*performance_mark);
return performance_mark; if (RuntimeEnabledFeatures::CustomUserTimingEnabled())
return performance_mark;
return nullptr;
} }
void Performance::clearMarks(const AtomicString& mark_name) { void Performance::clearMarks(const AtomicString& mark_name) {
...@@ -690,11 +692,10 @@ PerformanceMeasure* Performance::measure(ScriptState* script_state, ...@@ -690,11 +692,10 @@ PerformanceMeasure* Performance::measure(ScriptState* script_state,
ExceptionState& exception_state) { ExceptionState& exception_state) {
LogMeasureStartToUma(MeasureParameterType::kUnprovided); LogMeasureStartToUma(MeasureParameterType::kUnprovided);
LogMeasureEndToUma(MeasureParameterType::kUnprovided); LogMeasureEndToUma(MeasureParameterType::kUnprovided);
return measureInternal(script_state, measure_name, return MeasureInternal(
NativeValueTraits<StringOrDouble>::NullValue(), script_state, measure_name,
NativeValueTraits<StringOrDouble>::NullValue(), NativeValueTraits<StringOrDoubleOrPerformanceMeasureOptions>::NullValue(),
ScriptValue::CreateNull(script_state), NativeValueTraits<StringOrDouble>::NullValue(), exception_state);
exception_state);
} }
PerformanceMeasure* Performance::measure( PerformanceMeasure* Performance::measure(
...@@ -704,7 +705,7 @@ PerformanceMeasure* Performance::measure( ...@@ -704,7 +705,7 @@ PerformanceMeasure* Performance::measure(
ExceptionState& exception_state) { ExceptionState& exception_state) {
LogMeasureStartToUma(StartOrOptionsToParameterType(start_or_options)); LogMeasureStartToUma(StartOrOptionsToParameterType(start_or_options));
LogMeasureEndToUma(MeasureParameterType::kUnprovided); LogMeasureEndToUma(MeasureParameterType::kUnprovided);
return measureInternal(script_state, measure_name, start_or_options, return MeasureInternal(script_state, measure_name, start_or_options,
NativeValueTraits<StringOrDouble>::NullValue(), NativeValueTraits<StringOrDouble>::NullValue(),
exception_state); exception_state);
} }
...@@ -717,7 +718,7 @@ PerformanceMeasure* Performance::measure( ...@@ -717,7 +718,7 @@ PerformanceMeasure* Performance::measure(
ExceptionState& exception_state) { ExceptionState& exception_state) {
LogMeasureStartToUma(StartOrOptionsToParameterType(start_or_options)); LogMeasureStartToUma(StartOrOptionsToParameterType(start_or_options));
LogMeasureEndToUma(EndToParameterType(end)); LogMeasureEndToUma(EndToParameterType(end));
return measureInternal(script_state, measure_name, start_or_options, end, return MeasureInternal(script_state, measure_name, start_or_options, end,
exception_state); exception_state);
} }
...@@ -738,7 +739,7 @@ PerformanceMeasure* Performance::measure( ...@@ -738,7 +739,7 @@ PerformanceMeasure* Performance::measure(
// When |end| is null in C++, we cannot tell whether |end| is null, undefined or // When |end| is null in C++, we cannot tell whether |end| is null, undefined or
// empty in JS from StringOrDouble, so we need |end_is_empty| to help // empty in JS from StringOrDouble, so we need |end_is_empty| to help
// distinguish between (null or undefined) and empty. // distinguish between (null or undefined) and empty.
PerformanceMeasure* Performance::measureInternal( PerformanceMeasure* Performance::MeasureInternal(
ScriptState* script_state, ScriptState* script_state,
const AtomicString& measure_name, const AtomicString& measure_name,
const StringOrDoubleOrPerformanceMeasureOptions& start_or_options, const StringOrDoubleOrPerformanceMeasureOptions& start_or_options,
...@@ -755,9 +756,9 @@ PerformanceMeasure* Performance::measureInternal( ...@@ -755,9 +756,9 @@ PerformanceMeasure* Performance::measureInternal(
} }
const PerformanceMeasureOptions* options = const PerformanceMeasureOptions* options =
start_or_options.GetAsPerformanceMeasureOptions(); start_or_options.GetAsPerformanceMeasureOptions();
return measureInternal(script_state, measure_name, options->startTime(), return MeasureWithDetail(script_state, measure_name, options->startTime(),
options->endTime(), options->detail(), options->endTime(), options->detail(),
exception_state); exception_state);
} else { } else {
StringOrDouble converted_start; StringOrDouble converted_start;
if (start_or_options.IsDouble()) { if (start_or_options.IsDouble()) {
...@@ -770,11 +771,11 @@ PerformanceMeasure* Performance::measureInternal( ...@@ -770,11 +771,11 @@ PerformanceMeasure* Performance::measureInternal(
DCHECK(start_or_options.IsNull()); DCHECK(start_or_options.IsNull());
converted_start = NativeValueTraits<StringOrDouble>::NullValue(); converted_start = NativeValueTraits<StringOrDouble>::NullValue();
} }
// We let |end| behaves the same whether it's empty, undefined or null in // We let |end| behave the same whether it's empty, undefined or null in
// JS, as long as |end| is null in C++. // JS, as long as |end| is null in C++.
return measureInternal(script_state, measure_name, converted_start, end, return MeasureWithDetail(script_state, measure_name, converted_start, end,
ScriptValue::CreateNull(script_state), ScriptValue::CreateNull(script_state),
exception_state); exception_state);
} }
} else { } else {
// For consistency with UserTimingL2: the L2 API took |start| as a string, // For consistency with UserTimingL2: the L2 API took |start| as a string,
...@@ -804,14 +805,15 @@ PerformanceMeasure* Performance::measureInternal( ...@@ -804,14 +805,15 @@ PerformanceMeasure* Performance::measureInternal(
DCHECK(end.IsNull()); DCHECK(end.IsNull());
DCHECK(converted_end.IsNull()); DCHECK(converted_end.IsNull());
} }
measureInternal(script_state, measure_name, converted_start, converted_end, MeasureWithDetail(script_state, measure_name, converted_start,
ScriptValue::CreateNull(script_state), exception_state); converted_end, ScriptValue::CreateNull(script_state),
exception_state);
// Return nullptr to distinguish from L3. // Return nullptr to distinguish from L3.
return nullptr; return nullptr;
} }
} }
PerformanceMeasure* Performance::measureInternal( PerformanceMeasure* Performance::MeasureWithDetail(
ScriptState* script_state, ScriptState* script_state,
const AtomicString& measure_name, const AtomicString& measure_name,
const StringOrDouble& start, const StringOrDouble& start,
......
...@@ -255,19 +255,19 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { ...@@ -255,19 +255,19 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData {
void AddPaintTiming(PerformancePaintTiming::PaintType, TimeTicks start_time); void AddPaintTiming(PerformancePaintTiming::PaintType, TimeTicks start_time);
PerformanceMeasure* measureInternal( PerformanceMeasure* MeasureInternal(
ScriptState*, ScriptState*,
const AtomicString& measure_name, const AtomicString& measure_name,
const StringOrDoubleOrPerformanceMeasureOptions& start, const StringOrDoubleOrPerformanceMeasureOptions& start,
const StringOrDouble& end, const StringOrDouble& end,
ExceptionState&); ExceptionState&);
PerformanceMeasure* measureInternal(ScriptState*, PerformanceMeasure* MeasureWithDetail(ScriptState*,
const AtomicString& measure_name, const AtomicString& measure_name,
const StringOrDouble& start, const StringOrDouble& start,
const StringOrDouble& end, const StringOrDouble& end,
const ScriptValue& detail, const ScriptValue& detail,
ExceptionState&); ExceptionState&);
protected: protected:
Performance(TimeTicks time_origin, Performance(TimeTicks time_origin,
......
...@@ -64,13 +64,19 @@ interface Performance : EventTarget { ...@@ -64,13 +64,19 @@ interface Performance : EventTarget {
// User Timing // User Timing
// https://w3c.github.io/user-timing/#extensions-performance-interface // https://w3c.github.io/user-timing/#extensions-performance-interface
[MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] void mark(DOMString markName); // We use the returned value for feature detection:
// * L2 API returns null.
// * L3 API returns the created entry.
[MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMark? mark(DOMString markName);
[MeasureAs=UserTiming, CallWith=ScriptState, RuntimeEnabled=CustomUserTiming, RaisesException] PerformanceMark? mark(DOMString markName, (DOMHighResTimeStamp or PerformanceMarkOptions) startTimeOrPerformanceMarkOptions); [MeasureAs=UserTiming, CallWith=ScriptState, RuntimeEnabled=CustomUserTiming, RaisesException] PerformanceMark? mark(DOMString markName, (DOMHighResTimeStamp or PerformanceMarkOptions) startTimeOrPerformanceMarkOptions);
[MeasureAs=UserTiming] void clearMarks(optional DOMString markName = null); [MeasureAs=UserTiming] void clearMarks(optional DOMString markName = null);
// Doing either of the following requires enabling CustomUserTiming: // Doing either of the following requires enabling CustomUserTiming:
// * passing PerformanceMeasureOptions to |startOrOptions| // * passing PerformanceMeasureOptions to |startOrOptions|
// * passing timestamps to |startOrOptions| or |end| // * passing timestamps to |startOrOptions| or |end|
// We use the returned value for feature detection:
// * L2 API returns null.
// * L3 API returns the created entry.
// Custom User Timing (or User Timing L3) explainer: // Custom User Timing (or User Timing L3) explainer:
// https://docs.google.com/document/d/1hltt8z9C4PaI5Qu1YMIp1wOGdbJPJPoJwBarEeCY6xQ/edit#heading=h.ejti6qhmjv0b // https://docs.google.com/document/d/1hltt8z9C4PaI5Qu1YMIp1wOGdbJPJPoJwBarEeCY6xQ/edit#heading=h.ejti6qhmjv0b
[MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMeasure? measure(DOMString measureName, optional (DOMString or DOMHighResTimeStamp or PerformanceMeasureOptions)? startOrOptions, optional (DOMString or DOMHighResTimeStamp)? end); [MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMeasure? measure(DOMString measureName, optional (DOMString or DOMHighResTimeStamp or PerformanceMeasureOptions)? startOrOptions, optional (DOMString or DOMHighResTimeStamp)? end);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment