Commit 579d6501 authored by Yoav Weiss's avatar Yoav Weiss Committed by Commit Bot

Align resource timing buffer full processing to spec PR 168

This change implements the processing model from PR 168[1], when
it comes to setResourceTimingBufferSize(), clearResourceTimings()
and the firing of the resourcetimingbufferfull event.

[1] https://github.com/w3c/resource-timing/pull/168

Change-Id: I3a57196f10e0b4cf2bae5662b0e075673a0c2d80
Reviewed-on: https://chromium-review.googlesource.com/c/1345269
Commit-Queue: Yoav Weiss <yoavweiss@chromium.org>
Commit-Queue: Yoav Weiss <yoav@yoav.ws>
Reviewed-by: default avatarNicolás Peña Moreno <npm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610667}
parent 1152bb51
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="help" href="https://w3c.github.io/resource-timing/#dom-performance-setresourcetimingbuffersize">
<title>This test validates that setResourceTimingBufferFull behaves appropriately when set to the current buffer level.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
let eventFired = false;
function loadRandomResource() {
return fetch(window.location.href + "?" + Math.random());
}
setup(function() {
// Get the browser into a consistent state.
performance.clearResourceTimings();
performance.setResourceTimingBufferSize(3);
});
let gatherEntries = new Promise(function(resolve, reject) {
// Gather up 3 Resource Entries to kick off the rest of test behavior.
let resources = 0;
let observer = new PerformanceObserver(function(list) {
resources += list.getEntriesByType("resource").length;
if (resources !== 3)
return;
observer.disconnect();
resolve();
});
observer.observe({entryTypes: ["resource"]});
for (let i = 0; i < 3; ++i)
loadRandomResource();
});
let setBufferSize = new Promise(function(resolve, reject) {
performance.onresourcetimingbufferfull = function() {
eventFired = true;
performance.clearResourceTimings();
};
resolve();
});
promise_test(function() {
return gatherEntries;
}, "Reset the entries number");
promise_test(function() {
return setBufferSize;
}, "Set the buffer size");
promise_test(function() {
return new Promise(function(resolve, reject) {
loadRandomResource().then(resolve);
});
}, "Overflow the buffer");
/*
promise_test(function() {
return new Promise(function(resolve, reject) {
let waitForIt = function() {
if (eventFired) {
eventFired = false;
resolve();
}
}
step_timeout(waitForIt, 0);
});
}, "Wait for event");
*/
promise_test(function() {
return new Promise(function(resolve, reject) {
performance.clearResourceTimings();
loadRandomResource().then(function() {
resolve();
})});
}, "Clear and add another entry to the buffer");
promise_test(function() {
return new Promise(function(resolve, reject) {
let waitForIt = function() {
if (performance.getEntriesByType("resource").length) {
resolve();
} else {
reject("After buffer full, entry never added to primary");
}
}
step_timeout(waitForIt, 0);
});
}, "Wait for entry to be added");
</script>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
promise_test(()=>{
return new Promise((resolve, reject)=>{
const resource_timing_buffer_size = 1;
performance.clearResourceTimings();
var add_entry = function() {
performance.setResourceTimingBufferSize(resource_timing_buffer_size + 1);
// The sync attribute is added to the secondary buffer, so will be last one there and eventually dropped.
xhrScript("resources/empty.js?xhr");
resolve();
}
performance.addEventListener('resourcetimingbufferfull', add_entry);
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
appendScript('resources/empty.js');
// This resource overflows the entry buffer, and goes into the secondary buffer.
appendScript('resources/empty_script.js');
});
}, "Prepare test");
promise_test(function() {
return new Promise(function(resolve, reject) {
let waitForIt = function() {
resolve();
}
step_timeout(waitForIt, 0);
});
}, "Wait for next task");
promise_test(()=>{
return new Promise((resolve, reject)=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 2,
'Both entries should be stored in resource timing buffer since its increases size once it overflows.');
assert_true(entries[0].name.includes('empty.js'), "empty.js is in the entries buffer");
assert_true(entries[1].name.includes('empty_script.js'), "empty_script.js is in the entries buffer");
resolve();
});
}, "Test");
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const resource_timing_buffer_size = 1;
const t = async_test("Verify that adding entries to the resource timing buffer during resourcetimingbufferfull call works");
performance.clearResourceTimings();
var add_entry = function() {
performance.setResourceTimingBufferSize(resource_timing_buffer_size + 2);
xhrScript("resources/empty.js?xhr");
}
performance.addEventListener('resourcetimingbufferfull', add_entry);
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
appendScript('resources/empty.js');
// This resource overflows the entry buffer, and goes into the secondary buffer.
appendScript('resources/empty_script.js');
window.onload = t.step_func_done(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 3,
'All entries should be stored in resource timing buffer since its increases size once it overflows.');
assert_true(entries[0].name.includes('empty.js'), "empty.js is in the entries buffer");
assert_true(entries[1].name.includes('empty_script.js'), "empty_script.js is in the entries buffer");
assert_true(entries[2].name.includes('empty.js?xhr'), "empty.js?xhr is in the entries buffer");
});
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const resource_timing_buffer_size = 1;
const t = async_test("Verify that adding entries and then clearing the resource timing buffer results in entries added in the right order");
performance.clearResourceTimings();
performance.addEventListener('resourcetimingbufferfull', t.unreached_func("resourcetimingbufferfull should not fire"));
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
xhrScript('resources/empty.js?xhr');
// These resources overflow the entry buffer, and goes into the secondary buffer.
xhrScript('resources/empty.js?xhr2');
xhrScript('resources/empty.js?xhr3');
performance.clearResourceTimings();
performance.setResourceTimingBufferSize(3);
xhrScript('resources/empty.js?xhr4');
let entriesAfterAddition = performance.getEntriesByType('resource');
window.onload = t.step_timeout(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 3,
'the last 3 resources should be in the buffer, since the first one was cleared');
assert_true(entries[0].name.includes('empty.js?xhr2'), "empty.js?xhr2 is in the entries buffer");
assert_true(entries[1].name.includes('empty.js?xhr3'), "empty.js?xhr3 is in the entries buffer");
assert_true(entries[2].name.includes('empty.js?xhr4'), "empty.js?xhr4 is in the entries buffer");
assert_equals(entriesAfterAddition.length, 0, "No entries should have been added to the primary buffer before the task to 'fire a buffer full event'.");
t.done();
}, 0);
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates that decreasing the buffer size in onresourcetimingbufferfull callback does not result in extra entries being dropped.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const resource_timing_buffer_size = 2;
const t = async_test("Verify that reducing the size of the resource timing buffer during resourcetimingbufferfull call works");
performance.clearResourceTimings();
let resize = function() {
performance.setResourceTimingBufferSize(resource_timing_buffer_size - 1);
}
performance.addEventListener('resourcetimingbufferfull', resize);
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
xhrScript('resources/empty.js');
xhrScript('resources/empty.js?second');
// This resource overflows the entry buffer, and goes into the secondary buffer.
xhrScript('resources/empty_script.js');
window.onload = t.step_func_done(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 2,
'Both entries should be stored in resource timing buffer since it decreased its limit only after it overflowed.');
assert_true(entries[0].name.includes('empty.js'), "empty.js is in the entries buffer");
assert_true(entries[1].name.includes('empty.js?second'), "empty.js?second is in the entries buffer");
});
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates increasing the buffer size in onresourcetimingbufferfull callback of resource timing.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const resource_timing_buffer_size = 1;
const t = async_test("Verify that increasing the resource timing buffer during resourcetimingbufferfull call works");
performance.clearResourceTimings();
var increase = function() {
performance.setResourceTimingBufferSize(resource_timing_buffer_size * 2);
}
performance.addEventListener('resourcetimingbufferfull', increase);
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
appendScript('resources/empty.js');
// This resource overflows the entry buffer, and goes into the secondary buffer.
appendScript('resources/empty_script.js');
window.onload = t.step_func_done(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 2,
'Both entries should be stored in resource timing buffer since its increases size once it overflows.');
assert_true(entries[0].name.includes('empty.js'), "empty.js is in the entries buffer");
assert_true(entries[1].name.includes('empty_script.js'), "empty_script.js is in the entries buffer");
});
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates the buffer doesn't contain more entries than it should inside onresourcetimingbufferfull callback.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const t = async_test("Verify that inspecting the resource timing buffer during resourcetimingbufferfull call doesn't exceed the limit.");
let resource_timing_buffer_size = 2;
performance.clearResourceTimings();
var resize = function() {
assert_equals(performance.getEntriesByType("resource").length, resource_timing_buffer_size, "resource timing buffer in resourcetimingbufferfull is the size of the limit");
++resource_timing_buffer_size;
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
xhrScript("resources/empty.js?xhr");
assert_equals(performance.getEntriesByType("resource").length, resource_timing_buffer_size - 1, "A sync request was not added to the primary buffer just yet, because it is full");
++resource_timing_buffer_size;
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
}
performance.addEventListener('resourcetimingbufferfull', t.step_func(resize));
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// These resources gets buffered in the resource timing entry buffer.
appendScript('resources/empty.js');
appendScript('resources/empty.js?second');
// This resource overflows the entry buffer, and goes into the secondary buffer.
appendScript('resources/empty_script.js');
window.onload = t.step_func_done(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, resource_timing_buffer_size,
'All 4 entries should be stored in resource timing buffer.');
assert_true(entries[0].name.includes('empty.js'), "empty.js is in the entries buffer");
assert_true(entries[1].name.includes('empty.js?second'), "empty.js?second is in the entries buffer");
assert_true(entries[2].name.includes('empty_script.js'), "empty_script.js is in the entries buffer");
assert_true(entries[3].name.includes('empty.js?xhr'), "empty.js?xhr is in the entries buffer");
});
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="help" href="https://w3c.github.io/resource-timing/#dom-performance-setresourcetimingbuffersize">
<title>This test validates that setResourceTimingBufferFull behaves appropriately when set to the current buffer level.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
let eventFired = false;
function loadRandomResource() {
return fetch(window.location.href + "?" + Math.random());
}
setup(function() {
// Get the browser into a consistent state.
performance.clearResourceTimings();
performance.setResourceTimingBufferSize(100);
window.result = "";
});
let gatherEntries = new Promise(function(resolve, reject) {
// Gather up 3 Resource Entries to kick off the rest of test behavior.
let resources = 0;
let observer = new PerformanceObserver(function(list) {
resources += list.getEntriesByType("resource").length;
if (resources !== 3)
return;
observer.disconnect();
resolve();
});
observer.observe({entryTypes: ["resource"]});
for (let i = 0; i < 3; ++i)
loadRandomResource();
});
let setBufferSize = new Promise(function(resolve, reject) {
performance.onresourcetimingbufferfull = function() {
eventFired = true;
window.result += "Event Fired with " + performance.getEntriesByType("resource").length + " entries. ";
performance.clearResourceTimings();
};
window.result += "before setLimit(3). ";
performance.setResourceTimingBufferSize(3);
window.result += "after setLimit(3). ";
resolve();
});
promise_test(function() {
return gatherEntries;
}, "Reset the entries number");
promise_test(function() {
return setBufferSize;
}, "Set the buffer size");
promise_test(function() {
return new Promise(function(resolve, reject) {
loadRandomResource().then(function() {
window.result += "after loading 4th resource. ";
resolve();
})});
}, "Overflow the buffer");
promise_test(function() {
return new Promise(function(resolve, reject) {
let waitForIt = function() {
if (eventFired) {
resolve();
}
}
step_timeout(waitForIt, 0);
});
}, "Wait for event");
promise_test(function() {
return new Promise(function(resolve, reject) {
if (window.result != "before setLimit(3). after setLimit(3). after loading 4th resource. Event Fired with 3 entries. ") {
reject("Non matching value: " + window.result);
}
let entries = performance.getEntriesByType("resource");
if (entries.length != 1) {
reject("Number of entries in resource timing buffer is unexpected: " + entries.length);
}
resolve();
});
}, "Check result");
</script>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates the behavior of read and clear operation in onresourcetimingbufferfull callback of resource timing.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const t = async_test("Verify that clearing the resource timing buffer and storing the entries during resourcetimingbufferfull call works");
const resource_timing_buffer_size = 1;
performance.clearResourceTimings();
let global_buffer = [];
let store_and_clear = function() {
const entryList = performance.getEntriesByType('resource');
entryList.forEach(function (entry) {
global_buffer.push(entry);
});
performance.clearResourceTimings();
}
performance.addEventListener('resourcetimingbufferfull', store_and_clear);
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
appendScript('resources/empty.js');
// This resource overflows the entry buffer, and goes into the secondary buffer.
appendScript('resources/empty_script.js');
window.onload = t.step_func_done(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 1,
"Only the last entry should be stored in resource timing buffer since it's cleared once it overflows.");
assert_equals(global_buffer.length, 1, '1 resource timing entry should be moved to global buffer.');
assert_true(global_buffer[0].name.includes('empty.js'), "empty.js is in the global buffer");
assert_true(entries[0].name.includes('empty_script.js'), "empty_script.js is in the entries buffer");
});
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/append.js"></script>
</head>
<body>
<script>
const resource_timing_buffer_size = 1;
const t = async_test("Verify that adding entries and then increasing the size of the resource timing buffer results in entries added in the right order");
performance.clearResourceTimings();
performance.addEventListener('resourcetimingbufferfull', t.unreached_func("resourcetimingbufferfull should not fire"));
performance.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
// This resource gets buffered in the resource timing entry buffer.
xhrScript('resources/empty.js?xhr');
// These resources overflow the entry buffer, and goes into the secondary buffer.
xhrScript('resources/empty.js?xhr2');
xhrScript('resources/empty.js?xhr3');
performance.setResourceTimingBufferSize(3);
window.onload = t.step_timeout(()=>{
let entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 3,
'All resources should be in the buffer, since its size was increased');
assert_true(entries[0].name.includes('empty.js?xhr'), "empty.js?xhr2 is in the entries buffer");
assert_true(entries[1].name.includes('empty.js?xhr2'), "empty.js?xhr3 is in the entries buffer");
assert_true(entries[2].name.includes('empty.js?xhr3'), "empty.js?xhr3 is in the entries buffer");
t.done();
}, 0);
</script>
</body>
</html>
...@@ -9,33 +9,29 @@ ...@@ -9,33 +9,29 @@
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script> <script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script> <script src="resources/webperftestharnessextension.js"></script>
<script src="resources/append.js"></script>
</head> </head>
<body onload=onload_test()> <body>
<script> <script>
const context = new PerformanceContext(performance); const context = new PerformanceContext(performance);
const bufferSize = 5; const t = async_test("Verify that resourcetimingbufferfull is properly invoked");
performance.clearResourceTimings();
let bufferSize = 2;
context.setResourceTimingBufferSize(bufferSize); context.setResourceTimingBufferSize(bufferSize);
let bufferFullCount = 0; let bufferFullCount = 0;
function buffer_full_callback() { function buffer_full_callback(e) {
assert_equals(e.bubbles, false, "Event bubbles attribute is false");
bufferFullCount++; bufferFullCount++;
} }
context.registerResourceTimingBufferFullCallback(buffer_full_callback); context.registerResourceTimingBufferFullCallback(t.step_func(buffer_full_callback));
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before. // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
function appendScript(src) {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
document.body.appendChild(script);
}
appendScript('resources/empty.js'); appendScript('resources/empty.js');
appendScript('resources/empty_script.js'); appendScript('resources/empty_script.js');
appendScript('resources/resource_timing_test0.js'); appendScript('resources/resource_timing_test0.js');
setup({ explicit_done: true }); window.onload = t.step_func_done(()=>{
function onload_test() { assert_equals(context.getEntriesByType('resource').length, bufferSize, 'There should only be |bufferSize| resource entries.');
test_equals(context.getEntriesByType('resource').length, bufferSize, 'There should only be |bufferSize| resource entries.'); assert_equals(bufferFullCount, 1, 'onresourcetimingbufferfull should have been invoked once.');
test_equals(bufferFullCount, 1, 'onresourcetimingbufferfull should have been invoked once buffer is full.'); });
done();
}
</script> </script>
</body> </body>
</html> </html>
This is a testharness.js-based test.
PASS There are 4 scripts, and setResourceTimingBufferSize does not reduce the size.
FAIL onresourcetimingbufferfull should not be invoked during setResourceTimingBufferSize. assert_equals: onresourcetimingbufferfull should not be invoked during setResourceTimingBufferSize. expected 0 but got 2
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS No entry should be stored in resource timing buffer since its cleared once an item arrived.
FAIL 6 resource timing entries should be moved to global buffer. assert_equals: 6 resource timing entries should be moved to global buffer. expected 6 but got 7
FAIL http://web-platform.test:8001/resource-timing/resources/empty.js is not expected to be in the Resource Timing buffer assert_unreached: Reached unreachable code
PASS http://web-platform.test:8001/resource-timing/resources/empty_script.js is expected to have initiatorType script
PASS http://web-platform.test:8001/resource-timing/resources/resource_timing_test0.js is expected to have initiatorType script
PASS http://web-platform.test:8001/resource-timing/resources/webperftestharness.js is expected to have initiatorType script
PASS http://web-platform.test:8001/resource-timing/resources/webperftestharnessextension.js is expected to have initiatorType script
PASS http://web-platform.test:8001/resources/testharness.js is expected to have initiatorType script
PASS http://web-platform.test:8001/resources/testharnessreport.js is expected to have initiatorType script
Harness: the test ran to completion.
<!DOCTYPE HTML>
<html>
<head onload>
<meta charset="utf-8" />
<title>This test validates the behavior of read and clear operation in onresourcetimingbufferfull callback of resource timing.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
</head>
<body onload=onload_test()>
<script>
const context = new PerformanceContext(performance);
const resource_timing_buffer_size = 1;
let global_buffer = [];
function store_and_clear() {
const entryList = context.getEntriesByType('resource');
entryList.forEach(function (entry) {
global_buffer.push(entry);
});
context.clearResourceTimings();
}
context.registerResourceTimingBufferFullCallback(store_and_clear);
context.setResourceTimingBufferSize(resource_timing_buffer_size);
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
function appendScript(src) {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
document.body.appendChild(script);
}
appendScript('resources/empty.js');
appendScript('resources/empty_script.js');
appendScript('resources/resource_timing_test0.js');
setup({ explicit_done: true });
function onload_test() {
test_equals(context.getEntriesByType('resource').length, 0, 'No entry should be stored in resource timing buffer since its cleared once an item arrived.');
// The entry for empty.js must not be in the global buffer, but all others should be.
test_equals(global_buffer.length, 6, '6 resource timing entries should be moved to global buffer.');
const index = window.location.pathname.lastIndexOf('resource-timing');
const pathname = window.location.pathname.substring(0, index);
let expected_entries = {};
expected_entries[pathname + 'resources/testharness.js'] = 'script';
expected_entries[pathname + 'resources/testharnessreport.js'] = 'script';
expected_entries[pathname + 'resource-timing/resources/webperftestharness.js'] = 'script';
expected_entries[pathname + 'resource-timing/resources/webperftestharnessextension.js'] = 'script';
expected_entries[pathname + 'resource-timing/resources/empty_script.js'] = 'script';
expected_entries[pathname + 'resource-timing/resources/resource_timing_test0.js'] = 'script';
test_resource_entries(global_buffer, expected_entries);
done();
}
</script>
</body>
</html>
function appendScript(src) {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
document.body.appendChild(script);
}
function xhrScript(src) {
var xhr = new XMLHttpRequest();
xhr.open("GET", src, false);
xhr.send(null);
}
...@@ -44,19 +44,13 @@ promise_test(function(test) { ...@@ -44,19 +44,13 @@ promise_test(function(test) {
assert_greater_than(entry.startTime, 0); assert_greater_than(entry.startTime, 0);
assert_greater_than(entry.responseEnd, entry.startTime); assert_greater_than(entry.responseEnd, entry.startTime);
} }
return Promise.race([ return new Promise(function(resolve) {
new Promise(function(resolve) {
performance.onresourcetimingbufferfull = _ => { performance.onresourcetimingbufferfull = _ => {
resolve('bufferfull'); resolve('bufferfull');
} }
performance.setResourceTimingBufferSize(expectedResources.length); performance.setResourceTimingBufferSize(expectedResources.length);
}), fetch('dummy.txt');
});
// Race the bufferfull event against another fetch. We should get the
// event before this completes. This allows us to detect a failure
// to dispatch the event without timing out the entire test.
fetch('dummy.txt').then(resp => resp.text())
]);
}) })
.then(function(result) { .then(function(result) {
assert_equals(result, 'bufferfull'); assert_equals(result, 'bufferfull');
......
<script>
if (window.testRunner)
testRunner.dumpAsText();
performance.onresourcetimingbufferfull = function() {
document.body.innerHTML = "PASS";
};
performance.setResourceTimingBufferSize(1);
</script>
<div style='border-image-source: url(#1); background-image: url(#2);'>
<script>
if (window.testRunner)
testRunner.dumpAsText();
performance.onresourcetimingbufferfull = function() {
document.body.appendChild(document.createTextNode("PASS"));
};
performance.setResourceTimingBufferSize(1);
</script>
<iframe src="resources/content-iframe.html"></iframe>
PASS successfullyParsed is true
TEST COMPLETE
PASS. No crash when stop loading on resource timing buffer full.
<html>
<body>
<script src="/js-test-resources/js-test.js"></script>
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
performance.addEventListener('resourcetimingbufferfull', function(event) {
document.body.innerHTML = "PASS. No crash when stop loading on resource timing buffer full.";
setTimeout("testRunner.notifyDone()", 0);
});
performance.setResourceTimingBufferSize(1);
</script>
</body>
</html>
<html>
<head>
<link rel="help" href="https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourceTiming/Overview.html#extensions-performance-interface">
<script src="/js-test-resources/js-test.js"></script>
<script>
description("This test checks that Performance inherits EventTarget and that addEventListener() works for resourcetimingbufferfull events.");
window.jsTestIsAsync = true;
var bufferFullCount = 0;
var parameter;
function onBufferFull(event) {
shouldBeNull('performance.onresourcetimingbufferfull');
parameter = event;
shouldBe('parameter.__proto__', 'Event.prototype');
bufferFullCount++;
}
shouldBe('Performance.prototype.__proto__', 'EventTarget.prototype');
performance.addEventListener('resourcetimingbufferfull', onBufferFull);
shouldBeNull('performance.onresourcetimingbufferfull');
performance.setResourceTimingBufferSize(2);
</script>
</head>
<body>
<script>
function test() {
// Make sure the onBufferFull callback was called exactly 1 time.
shouldBe('bufferFullCount', '1');
performance.removeEventListener('resourcetimingbufferfull', onBufferFull);
finishJSTest();
}
window.onload = test;
</script>
<script src="resources/empty-script.js"></script>
</body>
</html>
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before. // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
let counter = performance.getEntriesByType("resource").length; let counter = performance.getEntriesByType("resource").length;
const documentFragment = document.createDocumentFragment(); const documentFragment = document.createDocumentFragment();
while (counter < default_buffer_size) { while (counter <= default_buffer_size) {
const src = "../loading/resources/empty.js?" + counter; const src = "../loading/resources/empty.js?" + counter;
const script = document.createElement('script'); const script = document.createElement('script');
script.type = 'text/javascript'; script.type = 'text/javascript';
......
...@@ -55,6 +55,7 @@ promise_test(function(test) { ...@@ -55,6 +55,7 @@ promise_test(function(test) {
return new Promise(function(resolve) { return new Promise(function(resolve) {
performance.onresourcetimingbufferfull = resolve; performance.onresourcetimingbufferfull = resolve;
performance.setResourceTimingBufferSize(expectedResources.length); performance.setResourceTimingBufferSize(expectedResources.length);
fetch('../../resources/dummy.txt');
}); });
}) })
.then(function() { .then(function() {
......
...@@ -177,15 +177,19 @@ constexpr size_t kDefaultEventTimingBufferSize = 150; ...@@ -177,15 +177,19 @@ constexpr size_t kDefaultEventTimingBufferSize = 150;
Performance::Performance( Performance::Performance(
TimeTicks time_origin, TimeTicks time_origin,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: resource_timing_buffer_size_(kDefaultResourceTimingBufferSize), : resource_timing_buffer_size_limit_(kDefaultResourceTimingBufferSize),
event_timing_buffer_max_size_(kDefaultEventTimingBufferSize), event_timing_buffer_max_size_(kDefaultEventTimingBufferSize),
user_timing_(nullptr), user_timing_(nullptr),
time_origin_(time_origin), time_origin_(time_origin),
observer_filter_options_(PerformanceEntry::kInvalid), observer_filter_options_(PerformanceEntry::kInvalid),
deliver_observations_timer_(std::move(task_runner), task_runner_(std::move(task_runner)),
deliver_observations_timer_(task_runner_,
this, this,
&Performance::DeliverObservationsTimerFired) { &Performance::DeliverObservationsTimerFired),
} resource_timing_buffer_full_timer_(
task_runner_,
this,
&Performance::FireResourceTimingBufferFull) {}
Performance::~Performance() = default; Performance::~Performance() = default;
...@@ -379,9 +383,7 @@ void Performance::clearResourceTimings() { ...@@ -379,9 +383,7 @@ void Performance::clearResourceTimings() {
} }
void Performance::setResourceTimingBufferSize(unsigned size) { void Performance::setResourceTimingBufferSize(unsigned size) {
resource_timing_buffer_size_ = size; resource_timing_buffer_size_limit_ = size;
if (IsResourceTimingBufferFull())
DispatchEvent(*Event::Create(event_type_names::kResourcetimingbufferfull));
} }
bool Performance::PassesTimingAllowCheck( bool Performance::PassesTimingAllowCheck(
...@@ -454,10 +456,6 @@ bool Performance::AllowsTimingRedirect( ...@@ -454,10 +456,6 @@ bool Performance::AllowsTimingRedirect(
void Performance::GenerateAndAddResourceTiming( void Performance::GenerateAndAddResourceTiming(
const ResourceTimingInfo& info, const ResourceTimingInfo& info,
const AtomicString& initiator_type) { const AtomicString& initiator_type) {
if (IsResourceTimingBufferFull() &&
!HasObserverFor(PerformanceEntry::kResource))
return;
ExecutionContext* context = GetExecutionContext(); ExecutionContext* context = GetExecutionContext();
const SecurityOrigin* security_origin = GetSecurityOrigin(context); const SecurityOrigin* security_origin = GetSecurityOrigin(context);
if (!security_origin) if (!security_origin)
...@@ -535,15 +533,20 @@ WebResourceTimingInfo Performance::GenerateResourceTiming( ...@@ -535,15 +533,20 @@ WebResourceTimingInfo Performance::GenerateResourceTiming(
void Performance::AddResourceTiming(const WebResourceTimingInfo& info, void Performance::AddResourceTiming(const WebResourceTimingInfo& info,
const AtomicString& initiator_type) { const AtomicString& initiator_type) {
if (IsResourceTimingBufferFull() &&
!HasObserverFor(PerformanceEntry::kResource))
return;
PerformanceEntry* entry = PerformanceEntry* entry =
PerformanceResourceTiming::Create(info, time_origin_, initiator_type); PerformanceResourceTiming::Create(info, time_origin_, initiator_type);
NotifyObserversOfEntry(*entry); NotifyObserversOfEntry(*entry);
if (!IsResourceTimingBufferFull()) // https://w3c.github.io/resource-timing/#dfn-add-a-performanceresourcetiming-entry
AddResourceTimingBuffer(*entry); if (CanAddResourceTimingEntry() &&
!resource_timing_buffer_full_event_pending_) {
resource_timing_buffer_.push_back(entry);
return;
}
if (!resource_timing_buffer_full_event_pending_) {
resource_timing_buffer_full_event_pending_ = true;
resource_timing_buffer_full_timer_.StartOneShot(TimeDelta(), FROM_HERE);
}
resource_timing_secondary_buffer_.push_back(entry);
} }
// Called after loadEventEnd happens. // Called after loadEventEnd happens.
...@@ -558,6 +561,35 @@ bool Performance::IsEventTimingBufferFull() const { ...@@ -558,6 +561,35 @@ bool Performance::IsEventTimingBufferFull() const {
return event_timing_buffer_.size() >= event_timing_buffer_max_size_; return event_timing_buffer_.size() >= event_timing_buffer_max_size_;
} }
void Performance::CopySecondaryBuffer() {
// https://w3c.github.io/resource-timing/#dfn-copy-secondary-buffer
while (resource_timing_secondary_buffer_.size() &&
CanAddResourceTimingEntry()) {
PerformanceEntry* entry = resource_timing_secondary_buffer_.front();
DCHECK(entry);
resource_timing_secondary_buffer_.pop_front();
resource_timing_buffer_.push_back(entry);
}
}
void Performance::FireResourceTimingBufferFull(TimerBase*) {
// https://w3c.github.io/resource-timing/#dfn-fire-a-buffer-full-event
while (resource_timing_secondary_buffer_.size()) {
int excess_entries_before = resource_timing_secondary_buffer_.size();
if (!CanAddResourceTimingEntry()) {
DispatchEvent(
*Event::Create(event_type_names::kResourcetimingbufferfull));
}
CopySecondaryBuffer();
int excess_entries_after = resource_timing_secondary_buffer_.size();
if (excess_entries_after >= excess_entries_before) {
resource_timing_secondary_buffer_.clear();
break;
}
}
resource_timing_buffer_full_event_pending_ = false;
}
void Performance::AddEventTimingBuffer(PerformanceEventTiming& entry) { void Performance::AddEventTimingBuffer(PerformanceEventTiming& entry) {
event_timing_buffer_.push_back(&entry); event_timing_buffer_.push_back(&entry);
...@@ -573,6 +605,7 @@ void Performance::clearEventTimings() { ...@@ -573,6 +605,7 @@ void Performance::clearEventTimings() {
event_timing_buffer_.clear(); event_timing_buffer_.clear();
} }
// TODO(yoav): EventTiming should follow a simpler buffering model.
void Performance::setEventTimingBufferMaxSize(unsigned size) { void Performance::setEventTimingBufferMaxSize(unsigned size) {
event_timing_buffer_max_size_ = size; event_timing_buffer_max_size_ = size;
if (IsEventTimingBufferFull()) if (IsEventTimingBufferFull())
...@@ -600,15 +633,9 @@ void Performance::AddPaintTiming(PerformancePaintTiming::PaintType type, ...@@ -600,15 +633,9 @@ void Performance::AddPaintTiming(PerformancePaintTiming::PaintType type,
NotifyObserversOfEntry(*entry); NotifyObserversOfEntry(*entry);
} }
void Performance::AddResourceTimingBuffer(PerformanceEntry& entry) { bool Performance::CanAddResourceTimingEntry() {
resource_timing_buffer_.push_back(&entry); // https://w3c.github.io/resource-timing/#dfn-can-add-resource-timing-entry
return resource_timing_buffer_.size() < resource_timing_buffer_size_limit_;
if (IsResourceTimingBufferFull())
DispatchEvent(*Event::Create(event_type_names::kResourcetimingbufferfull));
}
bool Performance::IsResourceTimingBufferFull() {
return resource_timing_buffer_.size() >= resource_timing_buffer_size_;
} }
void Performance::AddLongTaskTiming( void Performance::AddLongTaskTiming(
...@@ -961,6 +988,7 @@ void Performance::BuildJSONValue(V8ObjectBuilder& builder) const { ...@@ -961,6 +988,7 @@ void Performance::BuildJSONValue(V8ObjectBuilder& builder) const {
void Performance::Trace(blink::Visitor* visitor) { void Performance::Trace(blink::Visitor* visitor) {
visitor->Trace(resource_timing_buffer_); visitor->Trace(resource_timing_buffer_);
visitor->Trace(resource_timing_secondary_buffer_);
visitor->Trace(event_timing_buffer_); visitor->Trace(event_timing_buffer_);
visitor->Trace(navigation_timing_); visitor->Trace(navigation_timing_);
visitor->Trace(user_timing_); visitor->Trace(user_timing_);
......
...@@ -73,6 +73,7 @@ class PerformanceEventTiming; ...@@ -73,6 +73,7 @@ class PerformanceEventTiming;
class StringOrDoubleOrPerformanceMeasureOptions; class StringOrDoubleOrPerformanceMeasureOptions;
using PerformanceEntryVector = HeapVector<Member<PerformanceEntry>>; using PerformanceEntryVector = HeapVector<Member<PerformanceEntry>>;
using PerformanceEntryDeque = HeapDeque<Member<PerformanceEntry>>;
class CORE_EXPORT Performance : public EventTargetWithInlineData { class CORE_EXPORT Performance : public EventTargetWithInlineData {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
...@@ -269,6 +270,8 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { ...@@ -269,6 +270,8 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData {
const ScriptValue& detail, const ScriptValue& detail,
ExceptionState&); ExceptionState&);
void CopySecondaryBuffer();
protected: protected:
Performance(TimeTicks time_origin, Performance(TimeTicks time_origin,
scoped_refptr<base::SingleThreadTaskRunner>); scoped_refptr<base::SingleThreadTaskRunner>);
...@@ -279,8 +282,8 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { ...@@ -279,8 +282,8 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData {
return nullptr; return nullptr;
} }
bool IsResourceTimingBufferFull(); bool CanAddResourceTimingEntry();
void AddResourceTimingBuffer(PerformanceEntry&); void FireResourceTimingBufferFull(TimerBase*);
void NotifyObserversOfEntry(PerformanceEntry&) const; void NotifyObserversOfEntry(PerformanceEntry&) const;
void NotifyObserversOfEntries(PerformanceEntryVector&); void NotifyObserversOfEntries(PerformanceEntryVector&);
...@@ -290,7 +293,13 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { ...@@ -290,7 +293,13 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData {
virtual void BuildJSONValue(V8ObjectBuilder&) const; virtual void BuildJSONValue(V8ObjectBuilder&) const;
PerformanceEntryVector resource_timing_buffer_; PerformanceEntryVector resource_timing_buffer_;
unsigned resource_timing_buffer_size_; // The secondary RT buffer, used to store incoming entries after the main
// buffer is full, until the resourcetimingbufferfull event fires.
PerformanceEntryDeque resource_timing_secondary_buffer_;
unsigned resource_timing_buffer_size_limit_;
// A flag indicating that the buffer became full, the appropriate event was
// queued, but haven't yet fired.
bool resource_timing_buffer_full_event_pending_ = false;
PerformanceEntryVector event_timing_buffer_; PerformanceEntryVector event_timing_buffer_;
unsigned event_timing_buffer_max_size_; unsigned event_timing_buffer_max_size_;
Member<PerformanceEntry> navigation_timing_; Member<PerformanceEntry> navigation_timing_;
...@@ -305,7 +314,9 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData { ...@@ -305,7 +314,9 @@ class CORE_EXPORT Performance : public EventTargetWithInlineData {
HeapLinkedHashSet<TraceWrapperMember<PerformanceObserver>> observers_; HeapLinkedHashSet<TraceWrapperMember<PerformanceObserver>> observers_;
HeapLinkedHashSet<Member<PerformanceObserver>> active_observers_; HeapLinkedHashSet<Member<PerformanceObserver>> active_observers_;
HeapLinkedHashSet<Member<PerformanceObserver>> suspended_observers_; HeapLinkedHashSet<Member<PerformanceObserver>> suspended_observers_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
TaskRunnerTimer<Performance> deliver_observations_timer_; TaskRunnerTimer<Performance> deliver_observations_timer_;
TaskRunnerTimer<Performance> resource_timing_buffer_full_timer_;
}; };
} // namespace blink } // namespace blink
......
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