Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - cached resources generate performance entries</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that a 304 Not Modified resource appears in the
Performance Timeline.</p>
<script>
// Need to fetch the same resource twice; the first will get a 200 response but
// the second request should be cached and get a 304.
promise_test(async () => {
performance.clearResourceTimings();
const unique = Math.random();
const path = `resources/fake_responses.py?tag=${unique}`;
await load.xhr_sync(path);
await load.xhr_sync(path, {"If-None-Match": `${unique}`});
const entries = await new Promise(resolve => {
const accumulator = [];
new PerformanceObserver(entry_list => {
entry_list.getEntries().forEach(entry => {
accumulator.push(entry);
});
if (accumulator.length >= 2) {
resolve(accumulator);
}
}).observe({'type': 'resource', 'buffered': true});
});
if (entries.length != 2) {
throw new Error(`Expecting 2 but got ${entries.length} entries`);
}
assert_equals(entries[0].name, entries[1].name,
"Both entries should have the same name");
invariants.assert_tao_pass_no_redirect_http(entries[0]);
invariants.assert_tao_pass_304_not_modified_http(entries[1]);
}, "304 responses should still show up in the PerformanceTimeline");
</script>
</body>
</html>

View File

@ -0,0 +1,77 @@
For [Resource Timing][1] tests, we want to have a consistent and clear coding
style. The goals of this style are to:
* Make it easier for new contributors to find their way around
* Help improve readability and maintainability
* Help us understand which parts of the spec are tested or not
Lots of the following rules are arbitrary but the value is realized in
consistency instead of adhering to the 'perfect' style.
We want the test suite to be navigable. Developers should be able to easily
find the file or test that is relevant to their work.
* Tests should be arranged in files according to which piece of the spec they
test
* Files should be named using a consistent pattern
* HTML files should include useful meta tags
* `<title>` for controlling labels in results pages
* `<link rel="help">` to point at the relevant piece of the spec
We want the test suite to run consistently. Flaky tests are counterproductive.
* Prefer `promise_test` to `async_test`
* Note that theres [still potential for some concurrency][2]; use
`add_cleanup()` if needed
We want the tests to be readable. Tests should be written in a modern style
with recurring patterns.
* 80 character line limits where we can
* Consistent use of anonymous functions
* prefer
```
const func1 = param1 => {
body();
}
const func2 = (param1, param2) => {
body();
}
fn(param => {
body();
});
```
over
```
function func1(param1) {
body();
}
function func2(param1, param2) {
body();
}
fn(function(param) {
body();
});
```
* Prefer `const` (or, if needed, `let`) to `var`
* Contain use of .sub in filenames to known helper utilities where possible
* E.g. prefer use of get-host-info.sub.js to `{{host}}` or `{{ports[0]}}`
expressions
* Avoid use of webperftestharness[extension].js as its a layer of cognitive
overhead between test content and test intent
* Helper .js files are still encouraged where it makes sense but we want
to avoid a testing framework that is specific to Resource Timing (or
web performance APIs, in general).
* Prefer [`fetch_tests_from_window`][3] to collect test results from embedded
iframes instead of hand-rolled `postMessage` approaches
* Use the [`assert_*`][4] family of functions to check conformance to the spec
but throw exceptions explicitly when the test itself is broken.
* A failed assert indicates "the implementation doesn't conform to the
spec"
* Other uncaught exceptions indicate "the test case itself has a bug"
* Where possible, we want tests to be scalable - adding another test case
should be as simple as calling the tests with new parameters, rather than
copying an existing test and modifying it.
[1]: https://www.w3.org/TR/resource-timing-2/
[2]: https://web-platform-tests.org/writing-tests/testharness-api.html#promise-tests
[3]: https://web-platform-tests.org/writing-tests/testharness-api.html#consolidating-tests-from-other-documents
[4]: https://web-platform-tests.org/writing-tests/testharness-api.html#list-of-assertions

View File

@ -0,0 +1,6 @@
spec: https://w3c.github.io/resource-timing/
suggested_reviewers:
- plehegar
- zqzhang
- igrigorik
- yoavweiss

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates resource timing information for a same-origin=>cross-origin=>same-origin redirect chain without Timing-Allow-Origin.</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-cross-origin-resources"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
</head>
<body>
<script>
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
const SAME_ORIGIN = location.origin;
// Same-Origin => Cross-Origin => Same-Origin => Same-Origin redirect chain
let destUrl = `${SAME_ORIGIN}/resource-timing/resources/multi_redirect.py?`;
destUrl += `page_origin=${SAME_ORIGIN}`;
destUrl += `&cross_origin=${HTTPS_REMOTE_ORIGIN}`;
destUrl += `&final_resource=/resource-timing/resources/blank_page_green.htm`;
// No TAO in the redirect chain
attribute_test(
load.iframe, destUrl,
invariants.assert_cross_origin_redirected_resource,
"Verify that cross origin resources' timings are not exposed when " +
"same-origin=>cross-origin=>same-origin redirects have no " +
"`Timing-Allow-Origin:` headers.");
// Partial TAO in the redirect chain
destUrl += '&tao_steps=2';
attribute_test(
load.iframe, destUrl,
invariants.assert_cross_origin_redirected_resource,
"Verify that cross origin resources' timings are not exposed when " +
"same-origin=>cross-origin=>same-origin redirects have " +
"`Timing-Allow-Origin:` headers only on some of the responses.");
// Cross-origin => Cross-Origin => Same-Origin => Same-Origin redirect chain.
destUrl = `${HTTPS_REMOTE_ORIGIN}/resource-timing/resources/multi_redirect.py?`;
destUrl += `page_origin=${SAME_ORIGIN}`;
destUrl += `&cross_origin=${HTTPS_REMOTE_ORIGIN}`;
destUrl += `&final_resource=/resource-timing/resources/blue-with-tao.png`;
destUrl += `&tao_steps=3`;
// Full redirect chain with `TAO: *`.
attribute_test(
load.image, destUrl,
invariants.assert_tao_enabled_cross_origin_redirected_resource,
"Verify that cross origin resources' timings are exposed when cross-origin " +
"redirects have `Timing-Allow-Origin: *` headers");
// TAO with a specific origin
destUrl += `&tao_value=${SAME_ORIGIN}`;
attribute_test(
load.image, destUrl,
invariants.assert_cross_origin_redirected_resource,
"Verify that cross origin resources' timings are not exposed when " +
"same-origin=>cross-origin=>same-origin redirects have " +
"`Timing-Allow-Origin:` headers with a specific origin.");
</script>
</body>
</html>

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing TAO tests</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/custom-cors-response.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/tao-response.js"></script>
<body>
<script>
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
const run_test = (loader, resource_type) => {
attribute_test(loader, remote_tao_response(ORIGIN),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value contains only the origin. (${resource_type})`);
attribute_test(loader, remote_tao_response('*'),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value contains only a wildcard. (${resource_type})`);
attribute_test(loader, remote_tao_response(`${ORIGIN},fake`),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value list contains a case-sensitive match. (${resource_type})`);
attribute_test(loader, remote_tao_response(`${ORIGIN},*`),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value list contains the origin and a wildcard. (${resource_type})`);
attribute_test(loader, remote_tao_response('fake,*'),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value list contains a wildcard. (${resource_type})`);
attribute_test(loader, remote_tao_response('null'),
invariants.assert_tao_failure_resource,
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
`header value list contains a null origin. (${resource_type})`);
attribute_test(loader, remote_tao_response('*,*'),
invariants.assert_tao_pass_no_redirect_http,
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
`header value list contains multiple wildcards. (${resource_type})`);
attribute_test(loader, remote_tao_response(ORIGIN.toUpperCase()),
invariants.assert_tao_failure_resource,
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
`header value contains only the uppercased origin. (${resource_type})`);
attribute_test(loader, remote_tao_response(`${ORIGIN} *`),
invariants.assert_tao_failure_resource,
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
`header value contains the origin, a space, then a wildcard. ` +
`(${resource_type})`);
attribute_test(loader, custom_cors_response({}, REMOTE_ORIGIN),
invariants.assert_tao_failure_resource,
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
`header is not present. (${resource_type})`);
};
run_test(load.font, "font");
run_test(load.iframe, "iframe");
run_test(load.image, "image");
run_test(load.script, "script");
run_test(load.stylesheet, "stylesheet");
run_test(load.xhr_sync, "XMLHttpRequest");
</script>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TAO - port mismatch must fail the check</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-timing-allow-origin"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script>
const {ORIGINAL_HOST, PORT, PORT2} = get_host_info();
// The main page is being requested on the default port (PORT), while the
// subresource will be requested on a separate port (PORT2). The response will
// have a Timing-Allow-Origin header value with the second port so this page's
// origin should not be a match.
const port_mismatch_url = `${location.protocol}//${ORIGINAL_HOST}:${PORT2}` +
`/resource-timing/resources/TAOResponse.py?` +
`tao=origin_port_${PORT2}`;
attribute_test(
fetch, port_mismatch_url, invariants.assert_tao_failure_resource,
"A port mismatch must fail the TAO check");
// The same URL as above except the Timing-Allow-Origin header will have the
// same port as this page's origin. Therefore, this page's origin will match
// the Timing-Allow-Origin header's value. Therefore, the subresource's timings
// must be exposed.
const port_match_url = `${location.protocol}//${ORIGINAL_HOST}:${PORT2}` +
`/resource-timing/resources/TAOResponse.py?` +
`tao=origin_port_${PORT}`;
attribute_test(
fetch, port_match_url, invariants.assert_tao_pass_no_redirect_http,
"An identical port must pass the TAO check");
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that for a cross origin resource with different ports,
the timing allow check algorithm will fail when the value of
Timing-Allow-Origin value has the right host but the wrong port in it.</p>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#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>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async t => {
await forceBufferFullEvent();
performance.clearResourceTimings();
return new Promise(resolve => {
new PerformanceObserver(t.step_func(() => {
assert_equals(performance.getEntriesByType('resource').length, 1,
'The entry should be available in the performance timeline!');
resolve();
})).observe({type: 'resource'});
load.script(scriptResources[2]);
});
}, "Test that entry was added to the buffer after a buffer full event");
</script>

View File

@ -0,0 +1,29 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
await fillUpTheBufferWithSingleResource();
performance.addEventListener('resourcetimingbufferfull', () => {
performance.setResourceTimingBufferSize(2);
// The sync entry is added to the secondary buffer, so will be the last one there and eventually dropped.
load.xhr_sync(scriptResources[2]);
});
// This resource overflows the entry buffer, and goes into the secondary buffer.
load.script(scriptResources[1]);
await bufferFullFirePromise;
checkEntries(2);
}, "Test that entries synchronously added to the buffer during the callback are dropped");
</script>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
await fillUpTheBufferWithSingleResource();
performance.addEventListener('resourcetimingbufferfull', () => {
performance.setResourceTimingBufferSize(3);
load.xhr_sync(scriptResources[2]);
});
// This resource overflows the entry buffer, and goes into the secondary buffer.
load.script(scriptResources[1]);
await bufferFullFirePromise;
checkEntries(3);
}, "Test that entries synchronously added to the buffer during the callback don't get dropped if the buffer is increased");
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async t => {
addAssertUnreachedBufferFull(t);
await fillUpTheBufferWithSingleResource('resources/empty.js?willbelost');
// These resources overflow the entry buffer, and go into the secondary buffer.
load.xhr_sync(scriptResources[0]);
load.xhr_sync(scriptResources[1]);
performance.clearResourceTimings();
performance.setResourceTimingBufferSize(3);
load.xhr_sync(scriptResources[2]);
const entriesAfterAddition = performance.getEntriesByType('resource');
await waitForNextTask();
checkEntries(3);
assert_equals(entriesAfterAddition.length, 0, "No entries should have been added to the primary buffer before the task to 'fire a buffer full event'.");
}, "Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one");
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
performance.addEventListener('resourcetimingbufferfull', () => {
performance.setResourceTimingBufferSize(1);
});
await fillUpTheBufferWithTwoResources();
load.script(scriptResources[2]);
await bufferFullFirePromise;
checkEntries(2);
}, "Test that decreasing the buffer limit during the callback does not drop entries");
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<link rel="help"
href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
<title>This test validates that resource timing implementations have a finite
number of entries in their buffer.</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
promise_test(t => {
return new Promise(resolve => {
let counter = 0;
performance.onresourcetimingbufferfull = resolve;
const loadImagesRecursively = () => {
// Load an image.
(new Image()).src = "resources/blue.png?" + counter;
++counter;
// Yield to enable queueing an entry, then recursively load another image.
t.step_timeout(loadImagesRecursively, 0);
};
loadImagesRecursively();
});
}, "Finite resource timing entries buffer size");
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
await fillUpTheBufferWithSingleResource();
performance.addEventListener('resourcetimingbufferfull', () => {
performance.setResourceTimingBufferSize(2);
});
await load.script(scriptResources[1]);
await bufferFullFirePromise;
checkEntries(2);
}, "Test that increasing the buffer during the callback is enough for entries not to be dropped");
</script>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-setresourcetimingbuffersize"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async t => {
performance.addEventListener('resourcetimingbufferfull', t.step_func(() => {
assert_equals(performance.getEntriesByType("resource").length, 1,
"resource timing buffer in resourcetimingbufferfull is the size of the limit");
load.xhr_sync(scriptResources[2]);
performance.setResourceTimingBufferSize(3);
assert_equals(performance.getEntriesByType("resource").length, 1,
"A sync request must not be added to the primary buffer just yet, because it is full");
}));
await forceBufferFullEvent();
await waitForNextTask();
checkEntries(3);
}, "Test that entries in the secondary buffer are not exposed during the callback and before they are copied to the primary buffer");
</script>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>This test validates that setResourceTimingBufferFull behaves appropriately when set to the current buffer level.</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
let result = '';
performance.addEventListener('resourcetimingbufferfull', () => {
result += 'Event Fired with ' +
performance.getEntriesByType('resource').length + ' entries.';
performance.clearResourceTimings();
});
result += 'Before adding entries. ';
await fillUpTheBufferWithTwoResources();
result += 'After adding entries. ';
load.script(scriptResources[2]);
await bufferFullFirePromise;
assert_equals(result, 'Before adding entries. After adding entries. Event Fired with 2 entries.');
const entries = performance.getEntriesByType('resource');
assert_equals(entries.length, 1,
'Number of entries in resource timing buffer is unexpected');
assert_true(entries[0].name.includes(scriptResources[2]),
'The entry must correspond to the last resource loaded.')
}, "Test that adding entries and firing the buffer full event happen in the right order.");
</script>

View File

@ -0,0 +1,36 @@
<!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="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
await fillUpTheBufferWithSingleResource();
const entryBuffer = [];
performance.addEventListener('resourcetimingbufferfull', () => {
entryBuffer.push(...performance.getEntriesByType('resource'));
performance.clearResourceTimings();
});
load.script(scriptResources[1]);
await bufferFullFirePromise;
const 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_true(entries[0].name.includes(scriptResources[1]),
scriptResources[1] + " is in the entries buffer");
assert_equals(entryBuffer.length, 1,
'1 resource timing entry should be moved to entryBuffer.');
assert_true(entryBuffer[0].name.includes(scriptResources[0]),
scriptResources[0] + ' is in the entryBuffer');
}, "Test that entries overflowing the buffer trigger the buffer full event, can be stored, and make their way to the primary buffer after it's cleared in the buffer full event.");
</script>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates that reducing the buffer size after entries were
queued does not drop those entries, nor does it call the
resourcetimingbufferfull event callback.</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async t => {
addAssertUnreachedBufferFull(t);
await fillUpTheBufferWithTwoResources();
performance.setResourceTimingBufferSize(1);
await waitForNextTask();
checkEntries(2);
}, "Test that if the buffer is reduced after entries were added to it, those" +
" entries don't get cleared, nor is the resourcetimingbufferfull event" +
" being called.");
</script>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!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="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async t => {
addAssertUnreachedBufferFull(t);
await fillUpTheBufferWithSingleResource();
// These resources overflow the entry buffer, and go into the secondary buffer.
load.xhr_sync(scriptResources[1]);
load.xhr_sync(scriptResources[2]);
// Immediately increase the size: the bufferfull event should not be fired.
performance.setResourceTimingBufferSize(3);
await waitForNextTask();
checkEntries(3);
}, "Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event");
</script>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the functionality of onresourcetimingbufferfull in resource timing.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/buffer-full-utilities.js"></script>
</head>
<body>
<script>
promise_test(async () => {
let bufferFullCount = 0;
performance.addEventListener('resourcetimingbufferfull', e => {
assert_equals(e.bubbles, false, "Event bubbles attribute is false");
bufferFullCount++;
});
await fillUpTheBufferWithTwoResources();
// Overflow the buffer
await load.script(scriptResources[2]);
await waitForNextTask();
checkEntries(2);
assert_equals(bufferFullCount, 1, 'onresourcetimingbufferfull should have been invoked once.');
}, "Test that a buffer full event does not bubble and that resourcetimingbufferfull is called only once per overflow");
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
async_test(t => {
performance.clearResourceTimings();
// First observer creates second in callback to ensure the entry has been dispatched by the time
// the second observer begins observing.
new PerformanceObserver(() => {
// Second observer requires 'buffered: true' to see an entry.
new PerformanceObserver(t.step_func_done(list => {
const entries = list.getEntries();
assert_equals(entries.length, 1, 'There should be 1 resource entry.');
assert_equals(entries[0].entryType, 'resource');
assert_greater_than(entries[0].startTime, 0);
assert_greater_than(entries[0].responseEnd, entries[0].startTime);
assert_greater_than(entries[0].duration, 0);
assert_true(entries[0].name.endsWith('resources/empty.js'));
})).observe({'type': 'resource', buffered: true});
}).observe({'entryTypes': ['resource']});
fetch('resources/empty.js');
}, 'PerformanceObserver with buffered flag sees previous resource entries.');

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing: test behavior for cached resources</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/observe-entry.js"></script>
</head>
<body>
<h1>Description</h1>
<p>Test that a reused resource only appears in the buffer once.</p>
<script>
// Need our own image loading helper because the one in resource-loaders.js
// is designed to always side-step the HTTP cache but this test relies on the
// second request being resolved from the cache.
const load_image = path => new Promise(resolve => {
const img = document.createElement('img');
img.onload = img.onerror = () => resolve();
img.src = path;
document.body.append(img);
});
promise_test(async () => {
const blue = "resources/blue.png";
// First request. Should appear in the timeline.
await load_image(blue + "?cacheable");
// Second request. Should not appear in the timeline.
await load_image(blue + "?cacheable");
// Third request. When this request shows up in the timeline, we know that, if
// the second request would generate an entry, that entry would have already
// shown up in the timeline. Without this, we'd need to guess at how long to
// wait which tends to be flaky.
await load_image(blue + "?avoid-cache");
const entries = await new Promise(resolve => {
const accumulator = [];
new PerformanceObserver(entry_list => {
entry_list.getEntries().forEach(entry => {
if (!entry.name.includes("blue.png")) {
// Ignore resources other than blue images.
return;
}
accumulator.push(entry);
// Once we see the 'canary' resource, we don't need to wait anymore.
if (entry.name.endsWith('avoid-cache')) {
resolve(accumulator);
}
});
}).observe({'type': 'resource', 'buffered': true});
});
assert_equals(entries.length, 2, "There must be exactly 2 entries in the " +
"Performance Timeline");
assert_true(entries[0].name.endsWith("blue.png?cacheable"));
assert_true(entries[1].name.endsWith("blue.png?avoid-cache"));
}, "When a resource is resolved from cache, there must not be a " +
"corresponding entry in the Performance Timeline");
</script>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the functionality of clearResourceTimings method
in resource timing.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#dom-performance-clearresourcetimings">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(() => {
assert_equals(performance.getEntriesByType("resource").length, 2,
"Resource timing entries exist");
performance.clearResourceTimings();
assert_equals(performance.getEntriesByType("resource").length, 0,
"Resource timing entries are cleared");
}, "Test that clearResourceTimings() clears the performance timeline buffer");
</script>
</head>
</html>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing connection reuse</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/connection-reuse-test.js"></script>
<script>
const {HTTPS_ORIGIN} = get_host_info();
// Fetches the given subresource a couple times with the same connection.
const http_path = "resources/fake_responses.py";
connection_reuse_test(http_path,
{
'on_200': invariants.assert_tao_pass_no_redirect_http,
'on_304': invariants.assert_tao_pass_304_not_modified_http,
}, "Reuse HTTP connection");
// Like above, but the subresource is fetched over HTTPS while this page is
// fetched over HTTP.
const https_url = `${HTTPS_ORIGIN}/resource-timing/${http_path}`;
connection_reuse_test(https_url,
{
'on_200': invariants.assert_tao_pass_no_redirect_https,
'on_304': invariants.assert_tao_pass_304_not_modified_https,
}, "Reuse HTTPS connection from HTTP page");
// Like the above mixed-content test but the final resource is behind an HTTP
// redirect response.
const redirect_path = (() => {
// The resource behind the redirect is the same fake_responses.py handler
// on the HTTPS origin. Pass it through encodeURIComponent so that it can
// be passed through a query-parameter.
const redirect_url = encodeURIComponent(https_url)
// The request is made to the HTTPS origin with a query parameter that will
// cause a 302 response.
return `${https_url}?redirect=${redirect_url}`;
})();
connection_reuse_test(redirect_path,
{
'on_200': invariants.assert_tao_enabled_cross_origin_redirected_resource,
'on_304': invariants.assert_tao_enabled_cross_origin_redirected_resource,
}, "Reuse HTTPS connection with redirects from an HTTP page");
</script>
</head>
<body>
<h1>Description</h1>
<p>See <a href="resources/connection-reuse-test.js">the included test
script</a></p>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing connection reuse</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/connection-reuse-test.js"></script>
<script>
connection_reuse_test("resources/fake_responses.py",
{
'on_200': invariants.assert_tao_pass_no_redirect_https,
'on_304': invariants.assert_tao_pass_304_not_modified_https,
}, "Reuse an HTTPS connection");
</script>
</head>
<body>
<h1>Description</h1>
<p>See <a href="resources/connection-reuse-test.js">the included test
script</a></p>
</body>
</html>

View File

@ -0,0 +1,49 @@
// META: script=/common/utils.js
// META: script=/common/get-host-info.sub.js
// Because apache decrements the Keep-Alive max value on each request, the
// transferSize will vary slightly between requests for the same resource.
const fuzzFactor = 3; // bytes
const {HTTP_REMOTE_ORIGIN} = get_host_info();
const url = new URL('/resource-timing/resources/preflight.py',
HTTP_REMOTE_ORIGIN).href;
// The header bytes are expected to be > |minHeaderSize| and
// < |maxHeaderSize|. If they are outside this range the test will fail.
const minHeaderSize = 100;
const maxHeaderSize = 1024;
promise_test(async () => {
const checkCorsAllowed = response => response.arrayBuffer();
const requirePreflight = {headers: {'X-Require-Preflight' : '1'}};
const collectEntries = new Promise(resolve => {
let entriesSeen = [];
new PerformanceObserver(entryList => {
entriesSeen = entriesSeen.concat(entryList.getEntries());
if (entriesSeen.length > 2) {
throw new Error(`Saw too many PerformanceResourceTiming entries ` +
`(${entriesSeen.length})`);
}
if (entriesSeen.length == 2) {
resolve(entriesSeen);
}
}).observe({"type": "resource"});
});
// Although this fetch doesn't send a pre-flight request, the server response
// will allow cross-origin requests explicitly with the
// Access-Control-Allow-Origin header.
await fetch(url).then(checkCorsAllowed);
// This fetch will send a pre-flight request to do the CORS handshake
// explicitly.
await fetch(url, requirePreflight).then(checkCorsAllowed);
const entries = await collectEntries;
assert_greater_than(entries[0].transferSize, 0, 'No-preflight transferSize');
const lowerBound = entries[0].transferSize - fuzzFactor;
const upperBound = entries[0].transferSize + fuzzFactor;
assert_between_exclusive(entries[1].transferSize, lowerBound, upperBound,
'Preflighted transferSize');
}, 'PerformanceResourceTiming sizes fetch with preflight test');

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test ResourceTiming reporting for cross-origin iframe.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/observe-entry.js"></script>
</head>
<body>
<body>
<script>
const {REMOTE_ORIGIN} = get_host_info();
promise_test(async t => {
const iframe = document.createElement('iframe');
t.add_cleanup(() => iframe.remove());
iframe.src = `${REMOTE_ORIGIN}/resource-timing/resources/green.html`;
document.body.appendChild(iframe);
const entry = await observe_entry(iframe.src);
invariants.assert_tao_failure_resource(entry);
}, "A cross-origin iframe should report an opaque RT entry");
promise_test(async t => {
const iframe = document.createElement('iframe');
t.add_cleanup(() => iframe.remove());
iframe.src = `${REMOTE_ORIGIN}/resource-timing/resources/TAOResponse.py?tao=wildcard`;
document.body.appendChild(iframe);
const entry = await observe_entry(iframe.src);
invariants.assert_tao_pass_no_redirect_http(entry);
}, "A cross-origin iframe with TAO enabled should report a full RT entry");
</script>

View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the values in resource timing for cross-origin
redirects.</title>
<link rel="author" title="Intel" href="http://www.intel.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/custom-cors-response.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/tao-response.js"></script>
</head>
<body>
<script>
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
const HTTP_SO_to_XO_redirect_url = url => {
// Make an initial request to a same-domain resource that will return a 302
// redirect to the given (possibly cross-origin) url.
return `/resource-timing/resources/redirect-cors.py?location=${url}`;
};
const HTTP_SO_resource = () => {
if (location.protocol != "http:") {
throw new Error("Can only make an HTTP SO request if this page was " +
"served over HTTP.");
}
return tao_response("*", ORIGIN);
};
const HTTP_XO_redirect = (url, tao) => {
const ret = new URL(
`${REMOTE_ORIGIN}/resource-timing/resources/redirect-cors.py`);
ret.searchParams.append("location", url);
ret.searchParams.append("allow_origin", "*");
ret.searchParams.append("timing_allow_origin", tao);
return ret.href;
};
attribute_test(
load.iframe, HTTP_SO_to_XO_redirect_url(custom_cors_response({},
REMOTE_ORIGIN)),
invariants.assert_http_to_cross_origin_redirected_resource,
"Verify that cross-origin resources' timings aren't exposed through HTTP " +
"redirects.");
attribute_test(
load.iframe, HTTP_SO_to_XO_redirect_url(remote_tao_response("no-match")),
invariants.assert_cross_origin_redirected_resource,
"Verify that a redirected cross-origin resources' timings aren't exposed " +
"when the TAO check fails.");
attribute_test(
load.iframe, HTTP_SO_to_XO_redirect_url(remote_tao_response("*")),
invariants.assert_http_to_tao_enabled_cross_origin_https_redirected_resource,
"Verify that cross-origin resources' timings are exposed when the TAO " +
"check succeeds. Also verify that secureConnectionStart is 0 since the " +
"original request was over HTTP.");
attribute_test(
load.iframe, HTTP_XO_redirect(HTTP_XO_redirect(HTTP_SO_resource(), "*"), "*"),
invariants.assert_http_to_tao_enabled_cross_origin_https_redirected_resource,
"Verify that a redirect chain through cross-origin resources have their " +
"timings exposed when all TAO checks succeed. Also verify that " +
"secureConnectionStart is 0 since the original request was over HTTP.");
const failure_permutations = [
["fail", "fail", "fail"],
["fail", "fail", "*" ],
["fail", "*", "fail"],
["fail", "*", "*" ],
["*", "fail", "fail"],
["*", "fail", "*" ],
["*", "*", "fail"],
];
const test_case = (so_tao, xo1_tao, xo2_tao) => {
return HTTP_XO_redirect(HTTP_XO_redirect(HTTP_SO_resource(
so_tao), xo2_tao), xo1_tao);
};
const test_label = perm => {
return perm.map(x => {
if (x == "*" ) return "PASS";
if (x == "fail" ) return "FAIL";
throw new Error(`unexpected element ${x}`);
}).join(" -> ");
};
for (const permutation of failure_permutations) {
attribute_test(
load.iframe, test_case.apply(permutation),
invariants.assert_tao_failure_resource,
`Verify that a redirect chain through cross-origin resources do not have ` +
`their timings exposed when any of the TAO checks fail. ` +
`(${test_label(permutation)})`);
}
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the values in resource timing for cross-origin
redirects.</title>
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
</head>
<body>
<script>
const {REMOTE_ORIGIN} = get_host_info();
const delay = 2
const blank_page = `/resource-timing/resources/blank_page_green.htm`;
const destUrl = `/common/slow-redirect.py?delay=${delay}&location=${REMOTE_ORIGIN}/${blank_page}`;
const timeBefore = performance.now()
attribute_test(load.iframe, destUrl, entry => {
assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal');
assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching');
// See https://github.com/w3c/resource-timing/issues/264
assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays');
}, "Verify that cross-origin resources don't implicitly expose their redirect timings")
</script>
</body>
</html>

View File

@ -0,0 +1,70 @@
<!doctype html>
<html>
<head>
<title>Resource Timing: PerformanceResourceTiming attributes shouldn't change
if the HTTP status code changes</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src=/common/get-host-info.sub.js></script>
</head>
<body>
<img id="img_200">
<img id="img_307">
<img id="img_404">
<img id="img_502">
<script id="script_200"></script>
<script id="script_307"></script>
<script id="script_404"></script>
<script id="script_502"></script>
<script>
const listenForPerformanceEntries = num_expected => {
return new Promise(resolve => {
let results = [];
new PerformanceObserver(entryList => {
entryList.getEntries().forEach(entry => {
if (!entry.name.includes("status-code"))
return;
results.push(entry);
if (results.length == num_expected) {
resolve(results);
}
});
}).observe({entryTypes: ['resource']});
});
}
promise_test(async t => {
const destUrl = get_host_info().HTTP_REMOTE_ORIGIN + '/resource-timing/resources/';
const statusCodes = ['200', '307', '404', '502'];
let expected_entry_count = 0;
statusCodes.forEach(status => {
document.getElementById(`img_${status}`).src = `${destUrl}status-code.py?status=${status}`;
document.getElementById(`script_${status}`).src = `${destUrl}status-code.py?status=${status}&script=1`;
expected_entry_count += 2;
});
const entries = await listenForPerformanceEntries(expected_entry_count);
// We will check that the non-timestamp values of the entry match for all
// entries.
const keys = [
'entryType',
'nextHopProtocol',
'transferSize',
'encodedBodySize',
'decodedBodySize',
];
const first = entries[0];
entries.slice(1).forEach(entry => {
keys.forEach(attribute => {
assert_equals(entry[attribute], first[attribute],
`There must be no discernible difference for the ${attribute} ` +
`attribute but found a difference for the ${entry.name} resource.`);
})});
}, "Make sure cross origin resource fetch failures with different status codes are indistinguishable");
</script>

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script>
// Open a document on one of hosts on the web-platform test domain, so that
// document.domain will set a valid domain, turning the frame into a
// cross-origin frame.
const {OTHER_ORIGIN} = get_host_info();
const openee = window.open(OTHER_ORIGIN +
"/resource-timing/resources/document-domain-no-impact.html");
fetch_tests_from_window(openee);
</script>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates that a failed cross-origin fetch creates an opaque network timing entry.
</title>
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
</head>
<body>
<script>
const validDataURL = 'data:,Hello%2C%20World%21'
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTP_PORT} = get_host_info();
const validXmlUrl = '/common/dummy.xml';
network_error_entry_test(
`${REMOTE_ORIGIN}${validXmlUrl}`, null, `failed cross-origin requests`);
network_error_entry_test(`/common/redirect.py?location=${validDataURL}`, null, "non-HTTP redirect");
network_error_entry_test('//{{hosts[][nonexistent]}}/common/dummy.xml', null, "DNS failure");
network_error_entry_test(`http://${ORIGINAL_HOST}:${HTTP_PORT}/commo/dummy.xml`, null, "Mixed content");
network_error_entry_test('/common/dummy.xml', {cache: 'only-if-cached', mode: 'same-origin'},
"only-if-cached resource that was not cached");
network_error_entry_test(
`/element-timing/resources/multiple-redirects.py?redirect_count=22&final_resource=${validXmlUrl}`,
null, "too many redirects");
</script>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing: PerformanceResourceTiming attributes</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
<script>
attribute_test(
load.image, "resources/fake_responses.py#hash=1",
entry => {
assert_true(entry.name.includes('#hash=1'),
"There should be a hash in the resource name");
invariants.assert_tao_pass_no_redirect_http(entry);
},
"Image resources should generate conformant entries");
attribute_test(
load.font, "/fonts/Ahem.ttf",
invariants.assert_tao_pass_no_redirect_http,
"Font resources should generate conformant entries");
attribute_test(
load.image, "/common/redirect.py?location=resources/fake_responses.py",
invariants.assert_same_origin_redirected_resource,
"Same-origin redirects should populate redirectStart/redirectEnd");
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that PerformanceResourceTiming entries' attributes are
populated with the correct values.</p>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="timeout" content="long">
<title>Resource Timing: EventSource timing behavior</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
</head>
</script>
<script>
async_test(t => {
const repetitions = 2;
const url = new URL(`/eventsource/resources/message.py`, location.href);
const eventSource = new EventSource(url);
let messages = 0;
t.add_cleanup(() => eventSource.close());
eventSource.addEventListener('message', () => {
++messages;
})
new PerformanceObserver(() => {
const entries = performance.getEntriesByName(url);
assert_greater_than_equal(entries.length, messages - 1);
if (entries.length === repetitions)
t.done();
}).observe({type: 'resource'});
}, "ResourceTiming for EventSource should reflect number of re-connections to source");
</script>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test cross-origin fetch redirects have the right values.</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<script>
const {REMOTE_ORIGIN, ORIGIN} = get_host_info();
const redirect = "/common/redirect.py?" +
"location=/resource-timing/resources/green.html";
const cross_origin_redirect = REMOTE_ORIGIN + redirect;
const same_origin_redirect = ORIGIN + redirect;
attribute_test(
url => fetch(url, {mode: "no-cors", credentials: "include"}),
new URL(cross_origin_redirect).href,
invariants.assert_cross_origin_redirected_resource,
"Test fetching through a cross-origin redirect URL"
);
attribute_test(
url => fetch(url, {mode: "no-cors", credentials: "include"}),
new URL(same_origin_redirect).href,
invariants.assert_same_origin_redirected_resource,
"Test fetching through a same-origin redirect URL"
);
</script>

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test cross-origin fetch redirects have the right values.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
const load_font = url => {
document.body.innerHTML = `
<style>
@font-face {
font-family: ahem;
src: url('${url}');
}
</style>
<div style="font-family: ahem;">This fetches ahem font.</div>
`;
return document.fonts.ready;
};
const run_test = async (t, url) => {
// Set up PerformanceObserver
const href = new URL(url).href;
const setPerformanceObserver = new Promise(resolve => {
const po = new PerformanceObserver(resolve);
po.observe({type: "resource"});
});
// Load the font resource and wait for it to be fetched.
await load_font(href);
// Wait for an entry
const timeout = new Promise(resolve => t.step_timeout(resolve, 3000));
const list = await Promise.race([setPerformanceObserver, timeout]);
assert_equals(typeof(list), "object", "No iframe entry was fired");
const entries = list.getEntriesByName(url);
assert_equals(entries.length, 1);
// Test entry values
const entry = entries[0];
assert_greater_than(entry.fetchStart, 0, "fetchStart should be greater than 0 in redirects.");
assert_greater_than_equal(entry.domainLookupStart, entry.fetchStart, "domainLookupStart should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.domainLookupEnd, entry.domainLookupStart, "domainLookupEnd should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.connectStart, entry.domainLookupEnd, "connectStart should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.secureConnectionStart, entry.connectStart, "secureConnectionStart should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.connectEnd, entry.secureConnectionStart, "connectEnd should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.requestStart, entry.connectEnd, "requestStart should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.responseStart, entry.requestStart, "responseStart should be more than 0 in same-origin redirect.");
assert_greater_than_equal(entry.responseEnd, entry.responseStart, "responseEnd should be greater than 0 in redirects.");
assert_greater_than_equal(entry.duration, 0, "duration should be greater than 0 in redirects.");
}
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
promise_test(t => {
return run_test(t, HTTPS_REMOTE_ORIGIN + "/fonts/Ahem.ttf");
}, "Test a font's timestamps");
promise_test(t => {
return run_test(t, HTTPS_REMOTE_ORIGIN + "/resource-timing/resources/cors-ahem.py?pipe=trickle(d1)");
}, "Test a font's timestamps with delays");
</script>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting timing for frames.</title>
<frameset>
<frame src="resources/frameset-timing-frame.html" />
</frameset>

View File

@ -0,0 +1,24 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: timeout=long
'use strict';
// https://w3c.github.io/resource-timing/
idl_test(
['resource-timing'],
['performance-timeline', 'hr-time', 'dom', 'html'],
idl_array => {
try {
self.resource = performance.getEntriesByType('resource')[0];
} catch (e) {
// Will be surfaced when resource is undefined below.
}
idl_array.add_objects({
Performance: ['performance'],
PerformanceResourceTiming: ['resource']
});
}
);

View File

@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - test that unsuccessful iframes create entries</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href=
"https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<body>
<script>
// Like load.iframe but fetches the iframe under a "default-src 'none'"
// Content-Security-Policy.
const load_iframe_with_csp = async path => {
return load.iframe_with_attrs(path, {"csp": "default-src 'none'"});
};
// Runs a test (labeled by the given label) to verify that loading an iframe
// with the given URL generates a PerformanceResourceTiming entry and that the
// entry does not expose sensitive timing attributes.
const masked_entry_test = (url, label) => {
return attribute_test(load.iframe, url,
invariants.assert_tao_failure_resource, label);
};
// Runs a test (labeled by the given label) to verify that loading an iframe
// with the given URL generates a PerformanceResourceTiming entry and that the
// entry does expose sensitive timing attributes.
const unmasked_entry_with_csp_test = (url, label) => {
return attribute_test(load_iframe_with_csp, url,
invariants.assert_tao_pass_no_redirect_http, label);
};
// Runs a test (labeled by the given label) to verify that loading an iframe
// with the given URL under a "default-src 'none' Content-Security-Policy
// generates a PerformanceResourceTiming entry and that the entry does not
// expose sensitive timing attributes.
const masked_entry_with_csp_test = (url, label) => {
return attribute_test(load_iframe_with_csp, url,
invariants.assert_tao_failure_resource, label);
};
// Runs a test (labeled by the given label) to verify that loading an iframe
// with the given URL, an empty response body and under a "default-src 'none'
// Content-Security-Policy generates a PerformanceResourceTiming entry and that
// the entry does expose sensitive timing attributes.
const empty_unmasked_entry_with_csp_test = (url, label) => {
return attribute_test(load_iframe_with_csp, url,
invariants.assert_tao_pass_no_redirect_http_empty, label);
};
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTPS_PORT} = get_host_info();
const unhosted_url = `https://nonexistent.${ORIGINAL_HOST}:${HTTPS_PORT}/`;
masked_entry_test(
unhosted_url,
"Test iframe from non-existent host gets reported");
masked_entry_test(
"/resource-timing/resources/fake_responses.py?redirect=" + unhosted_url,
"Test iframe redirecting to non-existent host gets reported");
unmasked_entry_with_csp_test("/resource-timing/resources/csp-default-none.html",
"Same-origin iframe that complies with CSP attribute gets reported");
unmasked_entry_with_csp_test("/resource-timing/resources/green-frame.html",
"Same-origin iframe that doesn't comply with CSP attribute gets reported");
masked_entry_with_csp_test(
new URL("/resource-timing/resources/csp-default-none.html", REMOTE_ORIGIN),
"Cross-origin iframe that complies with CSP attribute gets reported");
masked_entry_with_csp_test(
new URL("/resource-timing/resources/green-frame.html", REMOTE_ORIGIN),
"Cross-origin iframe that doesn't comply with CSP attribute gets reported");
empty_unmasked_entry_with_csp_test(
"/resource-timing/resources/200_empty.asis",
"Same-origin empty iframe with a 200 status gets reported");
masked_entry_with_csp_test(
new URL("/resource-timing/resources/200_empty.asis", REMOTE_ORIGIN),
"Cross-origin empty iframe with a 200 status gets reported");
unmasked_entry_with_csp_test(
new URL("/resource-timing/resources/204_empty.asis"),
"Same-origin empty iframe with a 204 status gets reported");
unmasked_entry_with_csp_test(
new URL("/resource-timing/resources/205_empty.asis"),
"Same-origin empty iframe with a 205 status gets reported");
masked_entry_with_csp_test(
new URL("/resource-timing/resources/204_empty.asis", REMOTE_ORIGIN),
"Cross-origin empty iframe with a 204 status gets reported");
masked_entry_with_csp_test(
new URL("/resource-timing/resources/205_empty.asis", REMOTE_ORIGIN),
"Cross-origin empty iframe with a 205 status gets reported");
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting iframe timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<body>
<script>
function test(href, type) {
promise_test(async t => {
await load.iframe(href);
const entries = performance.getEntriesByType('resource').filter(({name}) => name.includes(href));
assert_equals(entries.length, 1);
assert_equals(entries[0].initiatorType, 'iframe');
}, `Iframes should report resource timing for ${type} iframes`);
}
test('/common/square.png', 'image');
test('/common/dummy.xhtml', 'xhtml');
test('/common/dummy.xml', 'xml');
test('/common/text-plain.txt', 'text');
</script>
</body>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting iframe timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<body>
<script>
promise_test(async t => {
const href = new URL('resources/redirect-without-location.py', location.href);
await load.iframe(href);
const entries = performance.getEntriesByType('resource').filter(({name}) => name.startsWith(href));
assert_equals(entries.length, 1);
assert_equals(entries[0].initiatorType, 'iframe');
}, 'Iframes should report resource timing for redirect responses without a location');
</script>
</body>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting iframe timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/frame-timing.js"></script>
<body>
<script>
test_frame_timing_before_load_event('iframe');
test_frame_timing_change_src('iframe');
</script>
</body>

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting iframe timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
<body>
<script>
promise_test(async t => {
const href = new URL('resources/download.asis', location.href);
const iframe = document.createElement('iframe');
iframe.src = href;
const errored = new Promise(resolve => iframe.addEventListener('error', resolve));
const loaded = new Promise(resolve => iframe.addEventListener('load', resolve));
document.body.appendChild(iframe);
const timeout = 2000;
t.add_cleanup(() => iframe.remove());
const expired = new Promise(resolve => t.step_timeout(resolve, timeout));
await Promise.any([loaded, expired, errored]);
const entries = performance.getEntriesByType('resource').filter(({name}) => name.startsWith(href));
assert_equals(entries.length, 0);
}, 'Iframes should not report resource timing for non-handled mime-types (downloads)');
</script>
</body>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting image timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
function test_image_sequence(src, event, t) {
const image = document.createElement('img');
const absoluteURL = new URL(src, location.href).toString();
document.body.appendChild(image);
t.add_cleanup(() => image.remove());
return new Promise(resolve => {
image.addEventListener(event, t.step_func(() => {
assert_equals(performance.getEntriesByName(absoluteURL).length, 1);
resolve();
}));
image.src = src;
});
}
promise_test(t => test_image_sequence('resources/blue.png', 'load', t),
"An image should receive its load event after the ResourceTiming entry is available");
promise_test(t => test_image_sequence('resources/nothing-at-all.png', 'error', t),
"A non-existent (404) image should receive its error event after the ResourceTiming entry is available");
promise_test(t => test_image_sequence('resources/invalid.png', 'error', t),
"An invalid image should receive its error event after the ResourceTiming entry is available");
</script>

View File

@ -0,0 +1,67 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates that the initiatorType information for various
Resource Timing entries is accurate for scripts.</title>
<link rel="help"
href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
<!-- Tested resources -->
<script src="resources/empty_script.js?id=blocking"></script>
<script src="resources/empty_script.js?id=async" async></script>
<script src="resources/empty_script.js?id=async_false" async=false></script>
<script src="resources/empty_script.js?id=defer" defer></script>
<script>
document.write("<script src='resources/empty_script.js?id=doc_written'></scr"
+ "ipt>");
const head = document.getElementsByTagName("head")[0];
const s1 = document.createElement("script");
s1.src = "empty_script.js?id=appended";
head.appendChild(s1);
const s2 = document.createElement("script");
s2.src = "empty_script.js?id=appended_async";
s2.async = true;
head.appendChild(s2);
const s3 = document.createElement("script");
s3.src = "empty_script.js?id=appended_aync_false";
s3.async = false;
head.appendChild(s3);
const s4 = document.createElement("script");
s4.src = "empty_script.js?id=appended_defer";
s4.defer = true;
head.appendChild(s4);
</script>
</head>
<body>
<script>
const wait_for_onload = () => {
return new Promise(resolve => {
window.addEventListener("load", resolve);
})};
promise_test(
async () => {
await wait_for_onload();
const entry_list = performance.getEntriesByType("resource");
for (entry of entry_list) {
if (entry.name.includes("empty_script.js")) {
assert_equals(entry.initiatorType, "script",
"initiatorType should be 'script' for " + entry.name);
}
}
}, "Validate initiatorType for scripts is 'script'");
</script>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: audio</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<audio src="/resource-timing/resources/empty.py?id=src"></audio>
<audio>
<source src="/resource-timing/resources/empty.py?id=source-wav"
type="audio/wav" />
</audio>
<audio>
<source src="/resource-timing/resources/empty.py?id=source-mpeg"
type="audio/mpeg" />
</audio>
<audio>
<source src="/resource-timing/resources/empty.py?id=source-ogg"
type="audio/ogg" />
</audio>
<script>
initiator_type_test("empty.py?id=src", "audio", "<audio src> without 'type' attribute");
initiator_type_test("empty.py?id=source-wav", "audio", "<source src> with type 'audio/wav'");
initiator_type_test("empty.py?id=source-mpeg", "audio", "<source src> with type 'audio/mpeg'");
initiator_type_test("empty.py?id=source-ogg", "audio", "<source src> with type 'audio/ogg'");
</script>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - initiatorType with dynamic insertion</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/entry-invariants.js"></script>
<script src="/resource-timing/resources/resource-loaders.js"></script>
<script>
const dynamic_initiator_type_test = (loader, path, expected_type,
resource_type) => {
attribute_test(loader, path, entry => {
assert_equals(entry.initiatorType, expected_type);
}, `A ${resource_type} should have the '${expected_type}' initiator type.`);
};
dynamic_initiator_type_test(load.image, "resources/resource_timing_test0.png",
"img", "image");
// Note that, to download a font, 'load.font' uses a <style> element to
// construct a font-face that is then applied to a <div>. Since it's a <style>
// element requesting the resource, the initiator type is 'css', not 'font'.
dynamic_initiator_type_test(load.font, "/fonts/Ahem.ttf", "css", "font");
dynamic_initiator_type_test(load.stylesheet,
"resources/resource_timing_test0.css", "link", "stylesheet");
dynamic_initiator_type_test(load.iframe, "resources/green.html", "iframe",
"iframe");
dynamic_initiator_type_test(load.script, "resources/empty.js", "script",
"script");
dynamic_initiator_type_test(load.xhr_sync, "resources/empty.py",
"xmlhttprequest", "XMLHttpRequest");
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that the initiatorType field is correct even when an
element is dynamically inserted.</p>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: embed</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<embed src="/resource-timing/resources/resource_timing_test0.css"
type="text/css">
<script>
initiator_type_test("resource_timing_test0.css", "embed", "<embed>");
</script>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: frameset</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
<script>
initiator_type_test("green.html", "frame", "<frame> in a <frameset>");
</script>
</head>
<!-- Although framesets were deprecated in HTML5, we still want to make sure
Resource Timing is emitting entries for the underlying resources' requests.
-->
<frameset>
<frame src="/resource-timing/resources/green.html">
</frameset>
</html>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: iframe</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<iframe src="/resource-timing/resources/green.html"></iframe>
<script>
initiator_type_test("green.html", "iframe", "<iframe>");
</script>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: img with srcset attribute</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<img src="/resource-timing/resources/resource_timing_test0.png"
srcset="/resource-timing/resources/resource_timing_test0.png?id=srcset 67w"
sizes="67px"></img>
<script>
initiator_type_test("resource_timing_test0.png?id=srcset", "img", "<img srcset>");
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: img</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<img src="/resource-timing/resources/resource_timing_test0.png"></img>
<script>
initiator_type_test("resource_timing_test0.png", "img", "<img>");
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: input</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<input type="image" src="/resource-timing/resources/resource_timing_test0.png">
<script>
initiator_type_test("resource_timing_test0.png", "input", "<input type=image>");
</script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: link</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<link rel="stylesheet" href="/resource-timing/resources/nested.css">
<link rel="prefetch"
href="/resource-timing/resources/resource_timing_test0.css?id=prefetch">
<link rel="preload" as="style"
href="/resource-timing/resources/resource_timing_test0.css?id=preload">
<link rel="prerender" href="/resource-timing/resources/green.html?id=prerender">
<link rel="manifest" href="/resource-timing/resources/manifest.json">
<link rel="modulepreload" href="resources/empty.js?id=modulePreload">
<script>
initiator_type_test("nested.css", "link", "<link>");
// Verify there are entries for each of nested.css' nested resources.
initiator_type_test("resource_timing_test0.css?id=n1", "css", "css resources embedded in css");
initiator_type_test("fonts/Ahem.ttf?id=n1", "css", "font resources embedded in css");
initiator_type_test("blue.png?id=n1", "css", "image resources embedded in css");
initiator_type_test("resource_timing_test0.css?id=prefetch", "link", "<link prefetch>");
initiator_type_test("resource_timing_test0.css?id=preload", "link", "<link preload>");
initiator_type_test("green.html?id=prerender", "link", "<link prerender>");
initiator_type_test("manifest.json", "link", "<link manifest>");
initiator_type_test("resources/empty.js?id=modulePreload", "other", "module preload");
</script>
<ol>This content forces a font to get fetched</ol>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: miscellaneous elements</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body background="/resource-timing/resources/blue.png?id=body">
<input type="image" src="/resource-timing/resources/blue.png?id=input">
<object type="image/png" data="/resource-timing/resources/blue.png?id=object">
</object>
<script>
navigator.sendBeacon('/resource-timing/resources/empty.py?id=beacon');
fetch('/resource-timing/resources/empty.py?id=fetch');
const evtSource = new EventSource('/resource-timing/resources/eventsource.py?id=eventsource');
</script>
<script>
initiator_type_test("blue.png?id=body", "body", "<body background>");
initiator_type_test("blue.png?id=input", "input", "<input type='image'>");
initiator_type_test("blue.png?id=object", "object", "<object type='image/png'>");
initiator_type_test("empty.py?id=beacon", "beacon", "sendBeacon()");
initiator_type_test("empty.py?id=fetch", "fetch", "for fetch()");
initiator_type_test("eventsource.py?id=eventsource", "other", "new EventSource()");
</script>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: picture</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<picture>
<source srcset="blue.png?id=picture-source" type="image/png" />
<img src="blue.png?id=picture-img" />
</picture>
<picture>
<source srcset="blue.png?id=picture-notsupported-source" type="image/notsupported" />
<img src="blue.png?id=picture-notsupported-img" />
</picture>
<picture>
<img src="blue.png?id=picture-img-src"
srcset="blue.png?id=picture-img-srcset"
sizes="67px"></img>
</picture>
<picture>
<img src="blue.png?id=picture-99x-img-src"
srcset="blue.png?id=picture-99x-img-srcset 99x"
sizes="67px"></img>
</picture>
<script>
initiator_type_test("blue.png?id=picture-source", "img", "<source> in a <picture>");
initiator_type_test("blue.png?id=picture-notsupported-img", "img", "<img> in a <picture>");
initiator_type_test("blue.png?id=picture-img-srcset", "img", "<img srcset> in a <picture>");
initiator_type_test("blue.png?id=picture-99x-img-src", "img", "<img src> in a <picture>");
</script>
</body>
</html>

View File

@ -0,0 +1,15 @@
if (observe_entry === undefined) {
throw new Error("You must include resource-timing/resources/observe-entry.js "
+ "before including this script.");
}
// Asserts that, for the given name, there is/will-be a
// PerformanceResourceTiming entry that has the given 'initiatorType'. The test
// is labeled according to the given descriptor.
const initiator_type_test = (entry_name, expected_initiator, descriptor) => {
promise_test(async () => {
const entry = await observe_entry(entry_name);
assert_equals(entry.initiatorType, expected_initiator);
}, `The initiator type for ${descriptor} must be '${expected_initiator}'`);
};

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: script</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<script src="/resource-timing/resources/empty_script.js"></script>
<script>
const async_xhr = new XMLHttpRequest;
async_xhr.open('GET', '/resource-timing/resources/blue.png?id=async_xhr',
true);
async_xhr.send();
</script>
<script>
initiator_type_test("empty_script.js", "script", "<script>");
initiator_type_test("blue.png?id=async_xhr", "xmlhttprequest", "an asynchronous XmlHTTPRequest");
</script>
</body>
</html>

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: style</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<style>
iframe {
background: url('/resource-timing/resources/blue.png?id=background');
}
body {
cursor: url('/resource-timing/resources/blue.png?id=cursor'), pointer;
}
ul {
list-style-image: url('/resource-timing/resources/blue.png?id=list-style');
}
@font-face {
font-family: remoteFontAhem;
src: url('/fonts/Ahem.ttf');
}
.ahem {
font-family: remoteFontAhem;
}
</style>
<iframe>This iframe forces the 'background' resource to be fetched.</iframe>
<ul>
<li>This content forces the 'list-style-image' resource to be fetched.</li>
</ul>
<div class="ahem">This content forces the '@font-face' resource to be fetched.</div>
<script>
initiator_type_test("blue.png?id=background", "css", "'background' attributes in <style> elements");
initiator_type_test("blue.png?id=cursor", "css", "'cursor' attributes in <style> elements");
initiator_type_test("blue.png?id=list-style", "css", "'list-style-image' attributes in <style> elements");
initiator_type_test("fonts/Ahem.ttf", "css", "'@font-face' resources");
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: svg</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<svg width=200 height=200
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<image href="/resource-timing/resources/blue.png" height="200" width="200"/>
</svg>
<script>
initiator_type_test("blue.png", "image", "<image> in an <svg>");
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiator type: video</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<body>
<video poster="/resource-timing/resources/blue.png?id=poster"></video>
<video src="/media/test.mp4?id=src" autoplay="true"></video>
<video autoplay="true">
<source src="/media/test.mp4?id=source-mp4" type="video/mp4">
<track kind="subtitles" srclang="en" default
src="/resource-timing/resources/empty.py?id=track">
</video>
<video autoplay="true">
<source src="/media/test.ogv?id=source-ogv" type="video/ogg">
</video>
<script>
initiator_type_test("blue.png?id=poster", "video", "<video poster>");
initiator_type_test("media/test.mp4?id=src", "video", "<video src>");
initiator_type_test("media/test.mp4?id=source-mp4", "video", "<source src> with type=\"video/mp4\"");
initiator_type_test("empty.py?id=track", "track", "<track src>");
initiator_type_test("media/test.ogv?id=source-ogv", "video", "<source src> with type=\"video/ogg\"");
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing initiatorType: worker resources</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
<script src="resources/initiator-type-test.js"></script>
</head>
<script>
const moduleWorkerURL = 'resources/empty.js?moduleWorker';
const workerURL = 'resources/empty.js?worker';
new Worker(moduleWorkerURL, {type: "module"});
new Worker(workerURL, {type: "classic"});
initiator_type_test(workerURL, "other", "classic worker");
initiator_type_test(moduleWorkerURL, "other", "module worker");
</script>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting input timing.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
async_test(t => {
const input = document.createElement('input');
input.type = "image";
const absoluteURL = new URL('resources/blue.png', location.href).toString();
t.add_cleanup(() => input.remove());
input.addEventListener('load', t.step_func(() => {
assert_equals(performance.getEntriesByName(absoluteURL).length, 1);
t.done();
}));
input.src = absoluteURL;
document.body.appendChild(input);
}, "An image input element should receive its load event after the ResourceTiming entry is available");
</script>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test the sequence of events when reporting link timing.</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
promise_test(async t => {
const link = document.createElement('link');
const delay = 500;
const src = `./resources/import.sub.css?delay=${delay}`
const absoluteURL = new URL(src, location.href).toString();
new PerformanceObserver(t.step_func(() => {
const allPerformanceEntries = performance.getEntriesByType('resource');
const linkEntry = allPerformanceEntries.find(e => e.name.includes('import.sub.css'));
const importEntry = allPerformanceEntries.find(e => e.name.includes('delay-css'));
if (!linkEntry || !importEntry)
return;
const linkEndTime = linkEntry.startTime + linkEntry.duration;
const importEndTime = importEntry.startTime + importEntry.duration;
assert_greater_than_equal(importEndTime, linkEndTime + delay, "link load should be done before import load");
t.done();
})).observe({type: 'resource'});
link.href = src;
link.rel = 'stylesheet';
document.head.appendChild(link);
}, "test that @imports don't affect link resource timings");
</script>

View File

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This tests transfer size of resource timing when loaded from memory cache.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/entry-invariants.js"></script>
</head>
<body>
<script>
function getScript(url) {
const script = document.createElement("script");
const loaded = new Promise(resolve => {
script.onload = script.onerror = resolve;
});
script.src = url;
document.body.appendChild(script);
return loaded;
}
function add_iframe(url) {
return new Promise(function (resolve) {
var frame = document.createElement('iframe');
frame.src = url;
frame.onload = function () { resolve(frame); };
document.body.appendChild(frame);
});
}
promise_test(async t => {
// Add unique token to url so that each run the url is different to avoid
// flakiness.
let url = 'resources/resource_timing_test0.js?unique=' +
Math.random().toString().substr(2);
let frame;
return add_iframe('resources/iframe-load-from-mem-cache-transfer-size.html')
.then((f) => {
frame = f;
// Load script onto iframe in order to get it into the memory cache.
return frame.contentWindow.getScript(url.split('/')[1])
})
.then(() => {
// Verify that the transferSize in case of normal load is greater than
// 0.
assert_positive_(
frame.contentWindow.performance.getEntriesByType('resource')
.filter(e => e.name.includes(url))[0], ['transferSize']);
// Load the same script onto the parent document. This time the script
// is coming from memory cache.
return getScript(url);
})
.then(() => {
// Verify that the transferSize in case of memory cache load is 0.
assert_zeroed_(
window.performance.getEntriesByType('resource')
.filter(e => e.name.includes(url))[0], ['transferSize']);
});
}, "The transferSize of resource timing entries should be 0 when resource \
is loaded from memory cache.");
</script>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name=timeout content=long>
<title>Resource Timing embed navigate - back button navigation</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/nested-contexts.js"></script>
<script>
open_test_window("resources/embed-navigate-back.html",
"Test that embed navigations are not observable by the parent, even " +
"after history navigations by the parent");
open_test_window("resources/embed-navigate-back.html?crossorigin",
"Test that crossorigin embed navigations are not observable by the " +
"parent, even after history navigations by the parent");
open_test_window("resources/embed-navigate-back.html?cross-site",
"Test that cross-site embed navigations are not observable by the " +
"parent, even after history navigations by the parent");
open_test_window("resources/embed-navigate.html",
"Test that embed navigations are not observable by the parent");
open_test_window("resources/embed-navigate.html?crossorigin",
"Test that crossorigin embed navigations are not observable by the parent");
open_test_window("resources/embed-navigate.html?cross-site",
"Test that cross-site embed navigations are not observable by the parent");
open_test_window("resources/embed-refresh.html",
"Test that embed refreshes are not observable by the parent");
open_test_window("resources/embed-refresh.html?crossorigin",
"Test that crossorigin embed refreshes are not observable by the parent");
open_test_window("resources/embed-refresh.html?cross-site",
"Test that cross-site embed refreshes are not observable by the parent");
</script>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name=timeout content=long>
<title>Resource Timing embed navigate - back button navigation</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/nested-contexts.js"></script>
<script>
open_test_window("resources/iframe-navigate-back.html",
"Test that iframe navigations are not observable by the parent, even after history navigations by the parent");
open_test_window("resources/iframe-navigate-back.html?crossorigin",
"Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent");
open_test_window("resources/iframe-navigate-back.html?cross-site",
"Test that cross-site iframe navigations are not observable by the parent, even after history navigations by the parent");
open_test_window("resources/iframe-navigate.html",
"Test that iframe navigations are not observable by the parent");
open_test_window("resources/iframe-navigate.html?crossorigin",
"Test that crossorigin iframe navigations are not observable by the parent");
open_test_window("resources/iframe-navigate.html?cross-site",
"Test that cross-site iframe navigations are not observable by the parent");
open_test_window("resources/iframe-refresh.html",
"Test that iframe refreshes are not observable by the parent");
open_test_window("resources/iframe-refresh.html?crossorigin",
"Test that crossorigin iframe refreshes are not observable by the parent");
open_test_window("resources/iframe-refresh.html?cross-site",
"Test that cross-site iframe refreshes are not observable by the parent");
</script>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name=timeout content=long>
<title>Resource Timing embed navigate - back button navigation</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/nested-contexts.js"></script>
<script>
open_test_window("resources/object-navigate-back.html",
"Test that object navigations are not observable by the parent, even " +
"after history navigations by the parent");
open_test_window("resources/object-navigate-back.html?crossorigin",
"Test that crossorigin object navigations are not observable by the " +
"parent, even after history navigations by the parent");
open_test_window("resources/object-navigate-back.html?cross-site",
"Test that cross-site object navigations are not observable by the " +
"parent, even after history navigations by the parent");
open_test_window("resources/object-navigate.html",
"Test that object navigations are not observable by the parent");
open_test_window("resources/object-navigate.html?crossorigin",
"Test that crossorigin object navigations are not observable by the " +
"parent");
open_test_window("resources/object-navigate.html?cross-site",
"Test that cross-site object navigations are not observable by the " +
"parent");
open_test_window("resources/object-refresh.html",
"Test that object refreshes are not observable by the parent");
open_test_window("resources/object-refresh.html?crossorigin",
"Test that crossorigin object refreshes are not observable by the parent");
open_test_window("resources/object-refresh.html?cross-site",
"Test that cross-site object refreshes are not observable by the parent");
</script>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - Check that nextHopProtocol is TAO protected</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/custom-cors-response.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/tao-response.js"></script>
</head>
<body>
<script>
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
const tao_protected_next_hop_test = (loader, item) => {
attribute_test(
loader, custom_cors_response({}, HTTPS_REMOTE_ORIGIN),
entry => assert_equals(entry.nextHopProtocol, "",
"nextHopProtocol should be the empty string."),
`Fetch TAO-less ${item} from remote origin. Make sure nextHopProtocol ` +
"is the empty string."
);
attribute_test(
loader, remote_tao_response('*'),
entry => assert_not_equals(entry.nextHopProtocol, "",
"nextHopProtocol should not be the empty string."),
`Fetch TAO'd ${item} from remote origin. Make sure nextHopProtocol ` +
"is not the empty string."
);
}
tao_protected_next_hop_test(load.font, "font");
tao_protected_next_hop_test(load.iframe, "iframe");
tao_protected_next_hop_test(load.image, "image");
tao_protected_next_hop_test(path => load.object(path, "text/plain"), "object");
tao_protected_next_hop_test(load.script, "script");
tao_protected_next_hop_test(load.stylesheet, "stylesheet");
tao_protected_next_hop_test(load.xhr_sync, "synchronous xhr");
tao_protected_next_hop_test(load.xhr_async, "asynchronous xhr");
</script>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Make sure that resources fetched by cross origin CSS are not in the timeline.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
const host = get_host_info();
const link = document.createElement("LINK");
link.rel = "stylesheet";
link.id = "cross_origin_style";
/*
This stylesheet is fetched from one of:
//www1.webplatform.test:64941/resource-timing/resources/nested.css
//127.0.0.1:64941/resource-timing/resources/nested.css
*/
link.href = "//" + host.REMOTE_HOST + ":{{ports[http][1]}}{{location[path]}}/../resources/nested.css"
document.currentScript.parentNode.insertBefore(link, document.currentScript);
</script>
<script>
const t = async_test("Make sure that resources fetched by cross origin CSS are not in the timeline.");
window.addEventListener("load", function() {
// A timeout is needed as entries are not guaranteed to be in the timeline before onload triggers.
t.step_timeout(function() {
const url = (new URL(document.getElementById("cross_origin_style").href));
const prefix = url.protocol + "//" + url.host;
assert_equals(performance.getEntriesByName(prefix + "/resource-timing/resources/resource_timing_test0.css?id=n1").length, 0, "Import should not be in timeline");
assert_equals(performance.getEntriesByName(prefix + "/fonts/Ahem.ttf?id=n1").length, 0, "Font should not be in timeline");
assert_equals(performance.getEntriesByName(prefix + "/resource-timing/resources/blue.png?id=n1").length, 0, "Image should not be in timeline");
t.done();
}, 200);
});
</script>
<ol>Some content</ol>
</body>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates that object resource emit resource timing entries.</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/entry-invariants.js"></script>
<script src="resources/resource-loaders.js"></script>
</head>
<body>
<script>
const load_image_object = async path => {
return load.object(path, "image/png");
}
const load_null_object = async path => {
return load.object(path, null);
}
attribute_test(
load_null_object, "resources/status-code.py?status=200&type=none",
invariants.assert_tao_pass_no_redirect_http,
"Verify that a 200 null-typed object emits an entry.");
attribute_test(
load_null_object, "resources/status-code.py?status=404&type=none",
invariants.assert_tao_pass_no_redirect_http,
"Verify that a 404 null-typed object emits an entry.");
attribute_test(
load_image_object, "resources/status-code.py?status=404&type=img",
invariants.assert_tao_pass_no_redirect_http,
"Verify that a 404 img-typed object emits an entry.");
</script>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates resource timing information for a timing allowed cross-origin redirect chain.</title>
<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=/common/get-host-info.sub.js></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
<script>
setup({explicit_done: true});
test_namespace('getEntriesByName');
function onload_test()
{
const context = new PerformanceContext(performance);
const entries = context.getEntriesByName(document.querySelector('object').data, 'resource');
test_equals(entries.length, 1, 'There should be one entry.');
const entry = entries[0];
test_greater_than(entry.redirectStart, 0, 'redirectStart > 0 in timing allowed cross-origin redirect.');
test_equals(entry.redirectStart, entry.startTime, 'redirectStart == startTime in timing allowed cross-origin redirect.');
test_greater_than(entry.redirectEnd, entry.redirectStart, 'redirectEnd > redirectStart in timing allowed cross-origin redirect.');
test_greater_or_equals(entry.fetchStart, entry.redirectEnd, 'fetchStart >= redirectEnd in timing allowed cross-origin redirect.');
done();
}
</script>
</head>
<body>
<script>
let destUrl = get_host_info().HTTP_REMOTE_ORIGIN + '/resource-timing/resources/multi_redirect.py?';
destUrl += 'page_origin=' + 'http://' + document.location.host;
destUrl += '&cross_origin=' + get_host_info().HTTP_REMOTE_ORIGIN;
destUrl += '&final_resource=' + encodeURIComponent("/resource-timing/resources/status-code.py?status=404&tao_value=*");
destUrl += '&tao_steps=3';
const objElement = document.createElement('object');
objElement.style = 'width: 0px; height: 0px;';
objElement.data = destUrl;
objElement.onerror = onload_test;
document.body.appendChild(objElement);
</script>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the values in resource timing for cross-origin
redirects.</title>
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
</head>
<body>
<script>
const {REMOTE_ORIGIN} = get_host_info();
const delay = 2
const not_found_page = encodeURIComponent("/resource-timing/resources/status-code.py?status=404");
const load_null_object = async path => {
return load.object(path, null);
}
const destUrl = `/common/slow-redirect.py?delay=${delay}&location=${REMOTE_ORIGIN}/${not_found_page}`;
const timeBefore = performance.now()
attribute_test(load_null_object, destUrl, entry => {
assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal');
assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching');
// See https://github.com/w3c/resource-timing/issues/264
assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays');
}, "Verify that cross-origin object resources don't implicitly expose their redirect timings")
</script>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing TAO - "null" and opaque origin</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that, for a cross origin resource, the timing allow
check algorithm will correctly distinguish between 'null' and 'Null' values in
the Timing-Allow-Origin header. An opaque origin's serialization is the string
"null" and the timing allow origin check needs to do a case-sensitive comparison
to the Timing-Allow-Origin header.
</p>
<iframe id="frameContext"></iframe>
<script>
const {ORIGIN} = get_host_info();
const url = `${ORIGIN}/resource-timing/resources/TAOResponse.py`;
const frame_content = `data:text/html;utf8,<body>
<script src="${ORIGIN}/resources/testharness.js"></` + `script>
<script src="${ORIGIN}/resource-timing/resources/entry-invariants.js">
</` + `script>
<script>
attribute_test(fetch, "${url}?tao=null",
invariants.assert_tao_pass_no_redirect_http,
"An opaque origin should be authorized to see resource timings when the" +
"TAO header is the string 'null'");
attribute_test(fetch, "${url}?tao=Null",
invariants.assert_tao_failure_resource,
"An opaque origin must not be authorized to see resource timings when " +
"the TAO header is the string 'Null'. (The check for 'null' must be " +
"case-sensitive)");
</` + `script>
</body>`;
frameContext.style = "display:none";
frameContext.src = frame_content;
fetch_tests_from_window(frameContext.contentWindow);
</script>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing Entry For hyperlink audit (ping)</title>
<link rel="help" href="https://w3c.github.io/resource-timing/"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resource-timing/resources/observe-entry.js"></script>
</head>
<body>
<script>
promise_test(async t => {
const link = document.createElement('a');
const delay = 500;
const ping = `/xhr/resources/delay.py?ms=${delay}`;
link.setAttribute('href', 'resources/close.html');
link.setAttribute('target', '_blank');
link.setAttribute('ping', ping);
link.innerText = 'Link';
document.body.appendChild(link);
link.click();
const entry = await observe_entry(ping);
assert_equals(entry.initiatorType, 'ping');
assert_greater_than(entry.duration, delay);
}, "Hyperlink auditing (<a ping>) should have a resource timing entry");
</script>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing: resources fetched through same-origin redirects</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/resource-loaders.js"></script>
<script src="resources/entry-invariants.js"></script>
<script>
const {HTTPS_NOTSAMESITE_ORIGIN} = get_host_info();
const redirect_url = `/common/redirect.py`;
const url_prefix = `${redirect_url}?location=/resource-timing/resources/`;
const https_url_prefix = `${redirect_url}?location=${HTTPS_NOTSAMESITE_ORIGIN}/resource-timing/resources/`;
attribute_test(
load.stylesheet, url_prefix + "resource_timing_test0.css",
invariants.assert_same_origin_redirected_resource,
"Verify attributes of a redirected stylesheet's PerformanceResourceTiming");
attribute_test(
load.image, url_prefix + "blue.png",
invariants.assert_same_origin_redirected_resource,
"Verify attributes of a redirected image's PerformanceResourceTiming");
attribute_test(
load.iframe, url_prefix + "green.html",
invariants.assert_same_origin_redirected_resource,
"Verify attributes of a redirected iframe's PerformanceResourceTiming");
attribute_test(
load.script, url_prefix + "empty_script.js",
invariants.assert_same_origin_redirected_resource,
"Verify attributes of a redirected script's PerformanceResourceTiming");
attribute_test(
load.xhr_sync, url_prefix + "green.html?id=xhr",
invariants.assert_same_origin_redirected_resource,
"Verify attributes of a redirected synchronous XMLHttpRequest's " +
"PerformanceResourceTiming");
attribute_test(
load.xhr_sync, https_url_prefix + "green.html?id=xhr",
invariants.assert_cross_origin_redirected_resource,
"Verify attributes of a synchronous XMLHttpRequest's " +
"PerformanceResourceTiming where the initial HTTP request is redirected " +
"to a cross-origin HTTPS resource."
);
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that, when a fetching resources that encounter
same-origin redirects, attributes of the PerformanceResourceTiming entry
conform to the specification.</p>
</body>
</html>

View File

@ -0,0 +1,222 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>This test validates the render blocking status of resources.</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<!-- Start of test cases -->
<script>
// Dynamic style using document.write in head
document.write(`
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-head-dynamic-docWrite'>
`);
document.write(`
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-head-dynamic-docWrite-print'
media=print>
`);
</script>
<link rel=stylesheet href="resources/empty_style.css?stylesheet-head">
<link rel=stylesheet href="resources/empty_style.css?stylesheet-head-media-print"
media=print>
<link rel="alternate stylesheet"
href="resources/empty_style.css?stylesheet-head-alternate">
<link rel=preload as=style href="resources/empty_style.css?link-style-head-preload">
<link rel=preload as=style href="resources/empty_style.css?link-style-preload-used">
<link rel=stylesheet href="resources/importer.css?stylesheet-importer-head">
<link rel=stylesheet id="link-head-remove-attr" blocking="render"
href="resources/empty_style.css?stylesheet-head-blocking-render-remove-attr">
<link rel=modulepreload href="resources/empty_script.js?link-head-modulepreload">
<style>@import url(resources/empty_style.css?stylesheet-inline-imported);</style>
<style media=print>
@import url(resources/empty_style.css?stylesheet-inline-imported-print);
</style>
</head>
<body>
<link rel=stylesheet href="resources/empty_style.css?stylesheet-body">
<link rel=stylesheet href="resources/importer.css?stylesheet-importer-body">
<link rel=stylesheet href="resources/empty_style.css?stylesheet-body-media-print"
media=print>
<link rel=stylesheet blocking="render"
href="resources/empty_style.css?stylesheet-body-blocking-render">
<!-- https://html.spec.whatwg.org/multipage/urls-and-fetching.html#blocking-attributes
mentions that an element is potentially render-blocking if its blocking
tokens set contains "render", or if it is implicitly potentially
render-blocking. By default, an element is not implicitly potentially
render-blocking.
https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
specifies that a link element of type stylesheet is implicitly potentially
render-blocking only if the element was created by its node document's parser. -->
<script>
// Dynamic style using document.write in body
document.write(`
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-body-dynamic-docWrite'>
`);
document.write(`
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-body-dynamic-docWrite-print'
media=print>
`);
// Dynamic style using innerHTML
document.head.innerHTML += `
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-head-dynamic-innerHTML'>
`;
document.head.innerHTML += `
<link rel=stylesheet
href='resources/empty_style.css?stylesheet-head-dynamic-innerHTML-print'
media=print>
`;
document.head.innerHTML += `
<link rel=stylesheet blocking=render
href='resources/empty_style.css?stylesheet-head-blocking-render-dynamic-innerHTML'>
`;
// Dynamic style using DOM API
var link = document.createElement("link");
link.href = "resources/empty_style.css?stylesheet-head-dynamic-dom";
link.rel = "stylesheet";
document.head.appendChild(link);
// Add a dynamic render-blocking style with DOM API
link = document.createElement("link");
link.href = "resources/empty_style.css?stylesheet-head-blocking-render-dynamic-dom";
link.rel = "stylesheet";
link.blocking = "render";
document.head.appendChild(link);
// Dynamic style preload using DOM API
link = document.createElement("link");
link.href = "resources/empty_style.css?link-style-head-preload-dynamic-dom";
link.rel = "preload";
link.as = "style";
document.head.appendChild(link);
// Dynamic module via modulepreload using DOM API
link = document.createElement("link");
link.href = "resources/empty_script.js?link-head-modulepreload-dynamic-dom";
link.rel = "modulepreload";
document.head.appendChild(link);
// Add a style preload with DOM API to be used later
link = document.createElement("link");
link.href = "resources/empty_style.css?link-style-preload-used-dynamic";
link.rel = "preload";
link.as = "style";
document.head.appendChild(link);
// Use the preload
link = document.createElement("link");
link.href = "resources/empty_style.css?link-style-preload-used-dynamic";
link.rel = "stylesheet";
document.head.appendChild(link);
// Dynamic inline CSS
// Add an inline CSS importer
document.write(`
<style>
@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-docwrite')
</style>
`);
document.write(`
<style media=print>
@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-docwrite-print')
</style>
`);
// Add a dynamic inline CSS importer using DOM API
let style = document.createElement("style");
style.textContent = "@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-dom')";
document.head.appendChild(style);
// Add a dynamic render-blocking inline CSS importer
style = document.createElement("style");
style.textContent = "@import url('resources/empty_style.css?stylesheet-inline-imported-blocking-render-dynamic-dom')";
style.blocking = "render";
document.head.appendChild(style);
// Dynamic CSS importer
document.write(`
<link rel=stylesheet href='resources/importer_dynamic.css'>
`);
document.write(`
<link rel=stylesheet href='resources/importer_print.css' media=print>
`);
// Removing blocking render attribute after request is made
const sheet = document.getElementById("link-head-remove-attr");
sheet.blocking = "";
</script>
<link rel=stylesheet href="resources/empty_style.css?link-style-preload-used">
<script>
const wait_for_onload = () => {
return new Promise(resolve => {
window.addEventListener("load", resolve);
})};
promise_test(
async () => {
const expectedRenderBlockingStatus = {
'stylesheet-head-dynamic-docWrite': 'blocking',
'stylesheet-head-dynamic-docWrite-print': 'non-blocking',
'stylesheet-head': 'blocking',
'stylesheet-head-media-print' : 'non-blocking',
'stylesheet-head-alternate' : 'non-blocking',
'link-style-head-preload' : 'non-blocking',
'stylesheet-importer-head' : 'blocking',
'stylesheet-head-blocking-render-remove-attr' : 'blocking',
'link-head-modulepreload' : 'non-blocking',
'stylesheet-inline-imported' : 'blocking',
'stylesheet-inline-imported-print' : 'non-blocking',
'stylesheet-body': 'non-blocking',
'stylesheet-importer-body' : 'non-blocking',
'stylesheet-body-media-print' : 'non-blocking',
'stylesheet-body-blocking-render' : 'non-blocking',
'stylesheet-body-dynamic-docWrite' : 'non-blocking',
'stylesheet-body-dynamic-docWrite-print': 'non-blocking',
'stylesheet-head-dynamic-innerHTML' : 'non-blocking',
'stylesheet-head-dynamic-innerHTML-print' : 'non-blocking',
'stylesheet-head-blocking-render-dynamic-innerHTML' : 'blocking',
'stylesheet-head-dynamic-dom' : 'non-blocking',
'stylesheet-head-blocking-render-dynamic-dom' : 'blocking',
'link-style-head-preload-dynamic-dom' : 'non-blocking',
'link-head-modulepreload-dynamic-dom' : 'non-blocking',
'link-style-preload-used' : 'non-blocking',
'link-style-preload-used-dynamic' : 'non-blocking',
'stylesheet-inline-imported-dynamic-docwrite': 'blocking',
'stylesheet-inline-imported-dynamic-docwrite-print' : 'non-blocking',
'stylesheet-inline-imported-dynamic-dom' : 'non-blocking',
'stylesheet-inline-imported-blocking-render-dynamic-dom' : 'blocking',
'stylesheet-imported' : 'blocking',
'stylesheet-imported-print' : 'non-blocking',
'stylesheet-imported-dynamic' : 'non-blocking'
};
await wait_for_onload();
const entry_list = performance.getEntriesByType("resource");
for (entry of entry_list) {
if (entry.name.includes("empty_style.css") ||
entry.name.includes("importer.css") ||
entry.name.includes("empty_script.js")) {
key = entry.name.split("?").pop();
expectedStatus = expectedRenderBlockingStatus[key];
assert_equals(entry.renderBlockingStatus, expectedStatus,
`render blocking status for ${entry.name} should be ${expectedStatus}`);
}
}
}, "Validate render blocking status of link resources in PerformanceResourceTiming");
</script>

View File

@ -0,0 +1,196 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>This test validates the render blocking status of resources.</title>
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<!-- Start of test cases -->
<script src="resources/empty_script.js?script-head"></script>
<script type="module" src="resources/empty_script.js?script-head-module"></script>
<script async type=module
src="resources/empty_script.js?script-head-async-module">
</script>
<script async src="resources/empty_script.js?script-head-async"></script>
<script defer src="resources/empty_script.js?script-head-defer"></script>
<script blocking=render
src="resources/empty_script.js?script-head-blocking-render">
</script>
<script async blocking=render
src="resources/empty_script.js?script-head-async-blocking-render">
</script>
<script type=module blocking=render
src="resources/empty_script.js?script-head-module-blocking-render">
</script>
<script async type=module blocking=render
src="resources/empty_script.js?script-head-async-module-blocking-render">
</script>
<script defer blocking=render
src="resources/empty_script.js?script-head-defer-blocking-render">
</script>
<script id="script-head-remove-attr" blocking=render
src="resources/empty_script.js?script-head-blocking-render-remove-attr">
</script>
<script>
document.write(`
<script defer
src="resources/empty_script.js?script-head-defer-dynamic-docwrite">
<\/script>`);
</script>
</head>
<body>
<script src="resources/empty_script.js?script-body"></script>
<script type="module" src="resources/empty_script.js?script-body-module"></script>
<script async type=module
src="resources/empty_script.js?script-body-async-module">
</script>
<script async src="resources/empty_script.js?script-body-async"></script>
<script defer src="resources/empty_script.js?script-body-defer"></script>
<script>
const script = document.createElement("script");
script.src = "resources/empty_script.js?script-head-dynamic-dom";
document.head.appendChild(script);
// Dynamic explicitly async script
const async_script = document.createElement("script");
async_script.src = "resources/empty_script.js?script-head-async-dynamic-dom";
async_script.async = true;
document.head.appendChild(async_script);
// Dynamic non-async script
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
// mentions that a script element has to be parser-inserted to be
// implicitly potentially render-blocking
const non_async_script = document.createElement("script");
non_async_script.src = "resources/empty_script.js?script-head-non-async-dynamic-dom";
non_async_script.async = false;
document.head.appendChild(non_async_script);
// Dynamic defer script
const defer_script = document.createElement("script");
defer_script.src = "resources/empty_script.js?script-head-defer-dynamic-dom";
defer_script.defer = true;
document.head.appendChild(defer_script);
// Dynamic explicitly render-blocking script
const blocking_script = document.createElement("script");
blocking_script.src = "resources/empty_script.js?script-head-blocking-render-dynamic-dom";
blocking_script.blocking = "render";
document.head.appendChild(blocking_script);
// Dynamic explicitly render-blocking module script
const blocking_module_script = document.createElement("script");
blocking_module_script.src = "resources/empty_script.js?script-head-module-blocking-render-dynamic-dom";
blocking_module_script.type = "module";
blocking_module_script.blocking = "render";
document.head.appendChild(blocking_module_script);
// Dynamic async module script
const async_module_script = document.createElement("script");
async_module_script.src = "resources/empty_script.js?script-head-async-module-dynamic-dom";
async_module_script.type = "module";
async_module_script.async = true;
document.head.appendChild(async_module_script);
// Dynamic async render-blocking module script
const async_blocking_module_script = document.createElement("script");
async_blocking_module_script.src = "resources/empty_script.js?script-head-async-module-blocking-render-dynamic-dom";
async_blocking_module_script.type = "module";
async_blocking_module_script.async = true;
async_blocking_module_script.blocking = "render"
document.head.appendChild(async_blocking_module_script);
// Add a module that imports more modules
const importer_script = document.createElement("script");
importer_script.src = "resources/fake_responses.py?url=importer.js";
importer_script.type = "module";
document.head.appendChild(importer_script);
// Add an async module that imports more modules
const importer_async_script = document.createElement("script");
importer_async_script.src = "resources/fake_responses.py?url=importer_async.js";
importer_async_script.type = "module";
importer_async_script.async = true;
document.head.appendChild(importer_async_script);
// Removing blocking render attribute after request is made
const script_element = document.getElementById("script-head-remove-attr");
script_element.blocking = "";
</script>
<script>
const wait_for_onload = () => {
return new Promise(resolve => {
window.addEventListener("load", resolve);
})};
promise_test(
async () => {
const expectedRenderBlockingStatus = {
'script-head': 'blocking',
'script-head-module' : 'non-blocking',
'script-head-async-module' : 'non-blocking',
'script-head-async' : 'non-blocking',
'script-head-defer' : 'non-blocking',
'script-head-blocking-render' : 'blocking',
'script-head-async-blocking-render' : 'blocking',
'script-head-module-blocking-render' : 'blocking',
'script-head-async-module-blocking-render' : 'blocking',
'script-head-defer-blocking-render' : 'blocking',
'script-head-blocking-render-remove-attr' : 'blocking',
'script-head-defer-dynamic-docwrite' : 'non-blocking',
'script-body' : 'non-blocking',
'script-body-module' : 'non-blocking',
'script-body-async-module' : 'non-blocking',
'script-body-async' : 'non-blocking',
'script-body-defer' : 'non-blocking',
'script-head-dynamic-dom': 'non-blocking',
'script-head-async-dynamic-dom' : 'non-blocking',
'script-head-non-async-dynamic-dom': 'non-blocking',
'script-head-defer-dynamic-dom' : 'non-blocking',
'script-head-blocking-render-dynamic-dom' : 'blocking',
'script-head-module-blocking-render-dynamic-dom' : 'blocking',
'script-head-async-module-dynamic-dom' : 'non-blocking',
'script-head-async-module-blocking-render-dynamic-dom' : 'blocking',
'script-head-import-defer' : 'non-blocking',
'script-head-import-defer-dynamic' : 'non-blocking',
'script-head-import-async' : 'non-blocking',
'script-head-import-async-dynamic' : 'non-blocking',
'script-importer' : 'non-blocking',
'script-importer-async' : 'non-blocking'
};
await wait_for_onload();
const entry_list = performance.getEntriesByType("resource");
for (entry of entry_list) {
if (entry.name.includes("empty_script.js")) {
key = entry.name.split("?").pop();
expectedStatus = expectedRenderBlockingStatus[key];
assert_equals(entry.renderBlockingStatus, expectedStatus,
`render blocking status for ${entry.name} should be ${expectedStatus}`);
}
else if (entry.name.includes("importer.js")){
key = 'script-importer';
expectedStatus = expectedRenderBlockingStatus[key];
assert_equals(entry.renderBlockingStatus, expectedStatus,
`render blocking status for ${entry.name} should be ${expectedStatus}`);
}
else if (entry.name.includes("importer_async.js")){
key = 'script-importer-async';
expectedStatus = expectedRenderBlockingStatus[key];
assert_equals(entry.renderBlockingStatus, expectedStatus,
`render blocking status for ${entry.name} should be ${expectedStatus}`);
}
}
}, "Validate render blocking status of script resources in PerformanceResourceTiming");
</script>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing ignores resources with data: URIs</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/resource-loaders.js"></script>
</head>
<body>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAOTm7AAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="></img>
<script>
promise_test(async t => {
const promise = new Promise(resolve => {
new PerformanceObserver(t.step_func(list => {
const entries = list.getEntries();
const dataEntries = entries.filter(e => e.name.includes('data:'));
assert_equals(dataEntries.length, 0, 'There must be no entry for `data: URL`.');
const blueEntries = entries.filter(e => e.name.includes('blue.png'));
if (blueEntries.length) {
// We can finish the test once we see the entry with blue.png.
resolve();
}
})).observe({entryTypes: ['resource']});
});
// Wait until the document is loaded.
await new Promise(resolve => {
window.addEventListener('load', resolve);
});
// Add the blue.png image after document is loaded to ensure we've received
// all of the previous Resource Timing entries.
load.image('blue.png');
return promise;
}, 'Resources with data: URIs must not be surfaced in Resource Timing');
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing - TAO on reload</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-timing-allow-origin"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<iframe id="test_iframe" src="resources/iframe-reload-TAO.html"></iframe>
<script>
window.onload = () => {
fetch_tests_from_window(test_iframe.contentWindow);
};
</script>
</body>

View File

@ -0,0 +1,517 @@
"use strict";
window.onload =
function () {
setup({ explicit_timeout: true });
/** Number of milliseconds to delay when the server injects pauses into the response.
This should be large enough that we can distinguish it from noise with high confidence,
but small enough that tests complete quickly. */
var serverStepDelay = 250;
var mimeHtml = "text/html";
var mimeText = "text/plain";
var mimePng = "image/png";
var mimeScript = "application/javascript";
var mimeCss = "text/css";
/** Hex encoding of a a 150x50px green PNG. */
var greenPng = "0x89504E470D0A1A0A0000000D494844520000006400000032010300000090FBECFD00000003504C544500FF00345EC0A80000000F49444154281563601805A36068020002BC00011BDDE3900000000049454E44AE426082";
/** Array containing test cases to run. Initially, it contains the one-off 'about:blank" test,
but additional cases are pushed below by expanding templates. */
var testCases = [
{
description: "No timeline entry for about:blank",
test:
function (test) {
// Insert an empty IFrame.
var frame = document.createElement("iframe");
// Wait for the IFrame to load and ensure there is no resource entry for it on the timeline.
//
// We use the 'createOnloadCallbackFn()' helper which is normally invoked by 'initiateFetch()'
// to avoid setting the IFrame's src. It registers a test step for us, finds our entry on the
// resource timeline, and wraps our callback function to automatically vet invariants.
frame.onload = createOnloadCallbackFn(test, frame, "about:blank",
function (initiator, entry) {
assert_equals(entry, undefined, "Inserting an IFrame with a src of 'about:blank' must not add an entry to the timeline.");
assertInvariants(
test,
function () {
test.done();
});
});
document.body.appendChild(frame);
// Paranoid check that the new IFrame has loaded about:blank.
assert_equals(
frame.contentWindow.location.href,
"about:blank",
"'Src' of new <iframe> must be 'about:blank'.");
}
},
];
// Create cached/uncached tests from the following array of templates. For each template entry,
// we add two identical test cases to 'testCases'. The first case initiates a fetch to populate the
// cache. The second request initiates a fetch with the same URL to cover the case where we hit
// the cache (if the caching policy permits caching).
[
{ initiator: "iframe", response: "(done)", mime: mimeHtml },
{ initiator: "xmlhttprequest", response: "(done)", mime: mimeText },
// Multiple browsers seem to cheat a bit and race onLoad of images. Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
// { initiator: "img", response: greenPng, mime: mimePng },
{ initiator: "script", response: '"";', mime: mimeScript },
{ initiator: "link", response: ".unused{}", mime: mimeCss },
]
.forEach(function (template) {
testCases.push({
description: "'" + template.initiator + " (Populate cache): The initial request populates the cache (if appropriate).",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl(
"mime:" + encodeURIComponent(template.mime)
+ "&send:" + encodeURIComponent(template.response),
/* allowCaching = */ true),
function (initiator, entry) {
test.done();
});
}
});
testCases.push({
description: "'" + template.initiator + " (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl(
"mime:" + encodeURIComponent(template.mime)
+ "&send:" + encodeURIComponent(template.response),
/* allowCaching = */ true),
function (initiator, entry) {
test.done();
});
}
});
});
// Create responseStart/responseEnd tests from the following array of templates. In this test, the server delays before
// responding with responsePart1, then delays again before completing with responsePart2. The test looks for the expected
// pauses before responseStart and responseEnd.
[
{ initiator: "iframe", responsePart1: serverStepDelay + "ms;", responsePart2: (serverStepDelay * 2) + "ms;(done)", mime: mimeHtml },
{ initiator: "xmlhttprequest", responsePart1: serverStepDelay + "ms;", responsePart2: (serverStepDelay * 2) + "ms;(done)", mime: mimeText },
// Multiple browsers seem to cheat a bit and race img.onLoad and setting responseEnd. Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
// { initiator: "img", responsePart1: greenPng.substring(0, greenPng.length / 2), responsePart2: "0x" + greenPng.substring(greenPng.length / 2, greenPng.length), mime: mimePng },
{ initiator: "script", responsePart1: '"', responsePart2: '";', mime: mimeScript },
{ initiator: "link", responsePart1: ".unused{", responsePart2: "}", mime: mimeCss },
]
.forEach(function (template) {
testCases.push({
description: "'" + template.initiator + ": " + serverStepDelay + "ms delay before 'responseStart', another " + serverStepDelay + "ms delay before 'responseEnd'.",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl(serverStepDelay + "ms" // Wait, then echo back responsePart1
+ "&mime:" + encodeURIComponent(template.mime)
+ "&send:" + encodeURIComponent(template.responsePart1)
+ "&" + serverStepDelay + "ms" // Wait, then echo back responsePart2
+ "&send:" + encodeURIComponent(template.responsePart2)),
function (initiator, entry) {
// Per https://w3c.github.io/resource-timing/#performanceresourcetiming:
// If no redirects (or equivalent) occur, this redirectStart/End must return zero.
assert_equals(entry.redirectStart, 0, "When no redirect occurs, redirectStart must be 0.");
assert_equals(entry.redirectEnd, 0, "When no redirect occurs, redirectEnd must be 0.");
// Server creates a gap between 'requestStart' and 'responseStart'.
assert_greater_than_equal(
entry.responseStart,
entry.requestStart + serverStepDelay,
"'responseStart' must be " + serverStepDelay + "ms later than 'requestStart'.");
// Server creates a gap between 'responseStart' and 'responseEnd'.
assert_greater_than_equal(
entry.responseEnd,
entry.responseStart + serverStepDelay,
"'responseEnd' must be " + serverStepDelay + "ms later than 'responseStart'.");
test.done();
});
}
});
});
// Create redirectEnd/responseStart tests from the following array of templates. In this test, the server delays before
// redirecting to a new synthetic response, then delays again before responding with 'response'. The test looks for the
// expected pauses before redirectEnd and responseStart.
[
{ initiator: "iframe", response: serverStepDelay + "ms;redirect;" + (serverStepDelay * 2) + "ms;(done)", mime: mimeHtml },
{ initiator: "xmlhttprequest", response: serverStepDelay + "ms;redirect;" + (serverStepDelay * 2) + "ms;(done)", mime: mimeText },
// Multiple browsers seem to cheat a bit and race img.onLoad and setting responseEnd. Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
// { initiator: "img", response: greenPng, mime: mimePng },
{ initiator: "script", response: '"";', mime: mimeScript },
{ initiator: "link", response: ".unused{}", mime: mimeCss },
]
.forEach(function (template) {
testCases.push({
description: "'" + template.initiator + " (Redirected): " + serverStepDelay + "ms delay before 'redirectEnd', another " + serverStepDelay + "ms delay before 'responseStart'.",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl(serverStepDelay + "ms" // Wait, then redirect to a second page that waits
+ "&redirect:" // before echoing back the response.
+ encodeURIComponent(
getSyntheticUrl(serverStepDelay + "ms"
+ "&mime:" + encodeURIComponent(template.mime)
+ "&send:" + encodeURIComponent(template.response)))),
function (initiator, entry) {
// Per https://w3c.github.io/resource-timing/#performanceresourcetiming:
// "[If redirected, startTime] MUST return the same value as redirectStart.
assert_equals(entry.startTime, entry.redirectStart, "startTime must be equal to redirectStart.");
// Server creates a gap between 'redirectStart' and 'redirectEnd'.
assert_greater_than_equal(
entry.redirectEnd,
entry.redirectStart + serverStepDelay,
"'redirectEnd' must be " + serverStepDelay + "ms later than 'redirectStart'.");
// Server creates a gap between 'requestStart' and 'responseStart'.
assert_greater_than_equal(
entry.responseStart,
entry.requestStart + serverStepDelay,
"'responseStart' must be " + serverStepDelay + "ms later than 'requestStart'.");
test.done();
});
}
});
});
// Ensure that responseStart only measures the time up to the first few
// bytes of the header response. This is tested by writing an HTTP 1.1
// status line, followed by a flush, then a pause before the end of the
// headers. The test makes sure that responseStart is not delayed by
// this pause.
[
{ initiator: "iframe", response: "(done)", mime: mimeHtml },
{ initiator: "xmlhttprequest", response: "(done)", mime: mimeText },
{ initiator: "script", response: '"";', mime: mimeScript },
{ initiator: "link", response: ".unused{}", mime: mimeCss },
]
.forEach(function (template) {
testCases.push({
description: "'" + template.initiator + " " + serverStepDelay + "ms delay in headers does not affect responseStart'",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl("status:200"
+ "&flush"
+ "&" + serverStepDelay + "ms"
+ "&mime:" + template.mime
+ "&send:" + encodeURIComponent(template.response)),
function (initiator, entry) {
// Test that the delay between 'responseStart' and
// 'responseEnd' includes the delay, which implies
// that 'responseStart' was measured at the time of
// status line receipt.
assert_greater_than_equal(
entry.responseEnd,
entry.responseStart + serverStepDelay,
"Delay after HTTP/1.1 status should not affect 'responseStart'.");
test.done();
});
}
});
});
// Test that responseStart uses the timing of 1XX responses by
// synthesizing a delay between a 100 and 200 status, and verifying that
// this delay is included before responseEnd. If the delay is not
// included, this implies that the 200 status line was (incorrectly) used
// for responseStart timing, despite the 100 response arriving earlier.
//
// Source: "In the case where more than one response is available for a
// request, due to an Informational 1xx response, the reported
// responseStart value is that of the first response to the last
// request."
[
{ initiator: "iframe", response: "(done)", mime: mimeHtml },
{ initiator: "xmlhttprequest", response: "(done)", mime: mimeText },
{ initiator: "script", response: '"";', mime: mimeScript },
{ initiator: "link", response: ".unused{}", mime: mimeCss },
]
.forEach(function (template) {
testCases.push({
description: "'" + template.initiator + " responseStart uses 1XX (first) response timings'",
test: function (test) {
initiateFetch(
test,
template.initiator,
getSyntheticUrl("status:100"
+ "&flush"
+ "&" + serverStepDelay + "ms"
+ "&status:200"
+ "&mime:" + template.mime
+ "&send:" + encodeURIComponent(template.response)),
function (initiator, entry) {
assert_greater_than_equal(
entry.responseEnd,
entry.responseStart + serverStepDelay,
"HTTP/1.1 1XX (first) response should determine 'responseStart' timing.");
test.done();
});
}
});
});
// Function to run the next case in the queue.
var currentTestIndex = -1;
function runNextCase() {
var testCase = testCases[++currentTestIndex];
if (testCase !== undefined) {
async_test(testCase.test, testCase.description);
}
}
// When a test completes, run the next case in the queue.
add_result_callback(runNextCase);
// Start the first test.
runNextCase();
/** Iterates through all resource entries on the timeline, vetting all invariants. */
function assertInvariants(test, done) {
// Multiple browsers seem to cheat a bit and race img.onLoad and setting responseEnd. Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
// Yield for 100ms to workaround a suspected race where window.onload fires before
// script visible side-effects from the wininet/urlmon thread have finished.
test.step_timeout(
test.step_func(
function () {
performance
.getEntriesByType("resource")
.forEach(
function (entry, index, entries) {
assertResourceEntryInvariants(entry);
});
done();
}),
100);
}
/** Assets the invariants for a resource timeline entry. */
function assertResourceEntryInvariants(actual) {
// Example from http://w3c.github.io/resource-timing/#resources-included:
// "If an HTML IFRAME element is added via markup without specifying a src attribute,
// the user agent may load the about:blank document for the IFRAME. If at a later time
// the src attribute is changed dynamically via script, the user agent may fetch the new
// URL resource for the IFRAME. In this case, only the fetch of the new URL would be
// included as a PerformanceResourceTiming object in the Performance Timeline."
assert_not_equals(
actual.name,
"about:blank",
"Fetch for 'about:blank' must not appear in timeline.");
assert_not_equals(actual.startTime, 0, "startTime");
// Per https://w3c.github.io/resource-timing/#performanceresourcetiming:
// "[If redirected, startTime] MUST return the same value as redirectStart. Otherwise,
// [startTime] MUST return the same value as fetchStart."
assert_in_array(actual.startTime, [actual.redirectStart, actual.fetchStart],
"startTime must be equal to redirectStart or fetchStart.");
// redirectStart <= redirectEnd <= fetchStart <= domainLookupStart <= domainLookupEnd <= connectStart
assert_less_than_equal(actual.redirectStart, actual.redirectEnd, "redirectStart <= redirectEnd");
assert_less_than_equal(actual.redirectEnd, actual.fetchStart, "redirectEnd <= fetchStart");
assert_less_than_equal(actual.fetchStart, actual.domainLookupStart, "fetchStart <= domainLookupStart");
assert_less_than_equal(actual.domainLookupStart, actual.domainLookupEnd, "domainLookupStart <= domainLookupEnd");
assert_less_than_equal(actual.domainLookupEnd, actual.connectStart, "domainLookupEnd <= connectStart");
// Per https://w3c.github.io/resource-timing/#performanceresourcetiming:
// "This attribute is optional. User agents that don't have this attribute available MUST set it
// as undefined. [...] If the secureConnectionStart attribute is available but HTTPS is not used,
// this attribute MUST return zero."
assert_true(actual.secureConnectionStart == undefined ||
actual.secureConnectionStart == 0 ||
actual.secureConnectionStart >= actual.connectEnd, "secureConnectionStart time");
// connectStart <= connectEnd <= requestStart <= responseStart <= responseEnd
assert_less_than_equal(actual.connectStart, actual.connectEnd, "connectStart <= connectEnd");
assert_less_than_equal(actual.connectEnd, actual.requestStart, "connectEnd <= requestStart");
assert_less_than_equal(actual.requestStart, actual.responseStart, "requestStart <= responseStart");
assert_less_than_equal(actual.responseStart, actual.responseEnd, "responseStart <= responseEnd");
}
/** Helper function to resolve a relative URL */
function canonicalize(url) {
var div = document.createElement('div');
div.innerHTML = "<a></a>";
div.firstChild.href = url;
div.innerHTML = div.innerHTML;
return div.firstChild.href;
}
/** Generates a unique string, used by getSyntheticUrl() to avoid hitting the cache. */
function createUniqueQueryArgument() {
var result =
"ignored_"
+ Date.now()
+ "-"
+ ((Math.random() * 0xFFFFFFFF) >>> 0)
+ "-"
+ syntheticRequestCount;
return result;
}
/** Count of the calls to getSyntheticUrl(). Used by createUniqueQueryArgument() to generate unique strings. */
var syntheticRequestCount = 0;
/** Return a URL to a server that will synthesize an HTTP response using the given
commands. (See SyntheticResponse.aspx). */
function getSyntheticUrl(commands, allowCache) {
syntheticRequestCount++;
var url =
canonicalize("./SyntheticResponse.py") // ASP.NET page that will synthesize the response.
+ "?" + commands; // Commands that will be used.
if (allowCache !== true) { // If caching is disallowed, append a unique argument
url += "&" + createUniqueQueryArgument(); // to the URL's query string.
}
return url;
}
/** Given an 'initiatorType' (e.g., "img") , it triggers the appropriate type of fetch for the specified
url and invokes 'onloadCallback' when the fetch completes. If the fetch caused an entry to be created
on the resource timeline, the entry is passed to the callback. */
function initiateFetch(test, initiatorType, url, onloadCallback) {
assertInvariants(
test,
function () {
log("--- Begin: " + url);
switch (initiatorType) {
case "script":
case "img":
case "iframe": {
var element = document.createElement(initiatorType);
document.body.appendChild(element);
element.onload = createOnloadCallbackFn(test, element, url, onloadCallback);
element.src = url;
break;
}
case "link": {
var element = document.createElement(initiatorType);
element.rel = "stylesheet";
document.body.appendChild(element);
element.onload = createOnloadCallbackFn(test, element, url, onloadCallback);
element.href = url;
break;
}
case "xmlhttprequest": {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = createOnloadCallbackFn(test, xhr, url, onloadCallback);
xhr.send();
break;
}
default:
assert_unreached("Unsupported initiatorType '" + initiatorType + "'.");
break;
}});
}
/** Used by 'initiateFetch' to register a test step for the asynchronous callback, vet invariants,
find the matching resource timeline entry (if any), and pass it to the given 'onloadCallback'
when invoked. */
function createOnloadCallbackFn(test, initiator, url, onloadCallback) {
// Remember the number of entries on the timeline prior to initiating the fetch:
var beforeEntryCount = performance.getEntriesByType("resource").length;
return test.step_func(
function() {
// If the fetch was initiated by XHR, we're subscribed to the 'onreadystatechange' event.
// Ignore intermediate callbacks and wait for the XHR to complete.
if (Object.getPrototypeOf(initiator) === XMLHttpRequest.prototype) {
if (initiator.readyState != 4) {
return;
}
}
var entries = performance.getEntriesByType("resource");
var candidateEntry = entries[entries.length - 1];
switch (entries.length - beforeEntryCount)
{
case 0:
candidateEntry = undefined;
break;
case 1:
// Per https://w3c.github.io/resource-timing/#performanceresourcetiming:
// "This attribute MUST return the resolved URL of the requested resource. This attribute
// MUST NOT change even if the fetch redirected to a different URL."
assert_equals(candidateEntry.name, url, "'name' did not match expected 'url'.");
logResourceEntry(candidateEntry);
break;
default:
assert_unreached("At most, 1 entry should be added to the performance timeline during a fetch.");
break;
}
assertInvariants(
test,
function () {
onloadCallback(initiator, candidateEntry);
});
});
}
/** Log the given text to the document element with id='output' */
function log(text) {
var output = document.getElementById("output");
output.textContent += text + "\r\n";
}
add_completion_callback(function () {
var output = document.getElementById("output");
var button = document.createElement('button');
output.parentNode.insertBefore(button, output);
button.onclick = function () {
var showButton = output.style.display == 'none';
output.style.display = showButton ? null : 'none';
button.textContent = showButton ? 'Hide details' : 'Show details';
}
button.onclick();
var iframes = document.querySelectorAll('iframe');
for (var i = 0; i < iframes.length; i++)
iframes[i].parentNode.removeChild(iframes[i]);
});
/** pretty print a resource timeline entry. */
function logResourceEntry(entry) {
log("[" + entry.entryType + "] " + entry.name);
["startTime", "redirectStart", "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd", "connectStart", "secureConnectionStart", "connectEnd", "requestStart", "responseStart", "responseEnd"]
.forEach(
function (property, index, array) {
var value = entry[property];
log(property + ":\t" + value);
});
log("\r\n");
}
};

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Resource-Timing Level 1</title>
<meta name="timeout" content="long">
<!-- To aid debugability, explicitly link the testharness's CSS to avoid demand
loading it while the test executes. -->
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<link rel="author" title="Microsoft" href="http://www.microsoft.com/">
<link rel="help" href="https://w3c.github.io/resource-timing/">
</head>
<body>
<div id="log"></div>
<pre id="output"></pre>
<script src="resource-timing-level1.js"></script>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing connection reuse</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
<script>
setup({explicit_done: true});
let iframe;
let d;
let body;
// Explicitly test the namespace before we start testing.
test_namespace('getEntriesByType');
function setup_iframe() {
iframe = document.getElementById('frameContext');
d = iframe.contentWindow.document;
iframe.addEventListener('load', onload_test, false);
}
function onload_test() {
const entries = iframe.contentWindow.performance.getEntriesByType('resource');
// When a persistent connection is used, follow-on resources should be included as PerformanceResourceTiming objects.
test_equals(entries.length, 2, 'There should be 2 PerformanceEntries');
if (entries.length >= 2) {
// When a persistent connection is used, for the resource that reuses the socket, connectStart and connectEnd should have the same value as fetchStart.
const entry = entries[1];
test_equals(entry.fetchStart, entry.connectStart, 'connectStart and fetchStart should be the same');
test_equals(entry.fetchStart, entry.connectEnd, 'connectEnd and fetchStart should be the same');
// secureConnectionStart is the same as fetchStart since the subresource is fetched over https
test_equals(entry.fetchStart, entry.secureConnectionStart, 'secureConnectionStart and fetchStart should be the same');
test_equals(entry.fetchStart, entry.domainLookupStart, 'domainLookupStart and fetchStart should be the same')
test_equals(entry.fetchStart, entry.domainLookupEnd, 'domainLookupEnd and fetchStart should be the same')
}
done();
}
window.setup_iframe = setup_iframe;
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that connectStart and connectEnd are the same when a connection is reused (e.g. when a persistent connection is used).</p>
<div id="log"></div>
<iframe id="frameContext" src="resources/fake_responses_https.sub.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing connection reuse</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
<script>
setup({explicit_done: true});
let iframe;
let d;
let body;
// Explicitly test the namespace before we start testing.
test_namespace('getEntriesByType');
function setup_iframe() {
iframe = document.getElementById('frameContext');
d = iframe.contentWindow.document;
iframe.addEventListener('load', onload_test, false);
}
function onload_test() {
const entries = iframe.contentWindow.performance.getEntriesByType('resource');
// When a persistent connection is used, follow-on resources should be included as PerformanceResourceTiming objects.
test_equals(entries.length, 2, 'There should be 2 PerformanceEntries');
if (entries.length >= 2) {
// When a persistent connection is used, for the resource that reuses the socket, connectStart and connectEnd should have the same value as fetchStart.
const entry = entries[1];
test_equals(entry.fetchStart, entry.connectStart, 'connectStart and fetchStart should be the same');
test_equals(entry.fetchStart, entry.connectEnd, 'connectEnd and fetchStart should be the same');
// secureConnectionStart is the same as fetchStart since the subresource is eventually redirected to https.
test_equals(entry.fetchStart, entry.secureConnectionStart, 'secureConnectionStart and fetchStart should be the same');
test_equals(entry.fetchStart, entry.domainLookupStart, 'domainLookupStart and fetchStart should be the same')
test_equals(entry.fetchStart, entry.domainLookupEnd, 'domainLookupEnd and fetchStart should be the same')
}
done();
}
window.setup_iframe = setup_iframe;
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that connectStart and connectEnd are the same when a connection is reused (e.g. when a persistent connection is used).</p>
<div id="log"></div>
<iframe id="frameContext" src="resources/fake_responses_https_redirect.sub.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing in dedicated workers</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
<link rel="stylesheet" href="resources/resource_timing_test0.css" />
<script>
setup({explicit_done: true});
const worker = new Worker("resources/worker_with_images.js");
worker.onmessage = function(event) {
const context = new PerformanceContext(window.performance);
const entries = context.getEntriesByType('resource');
test_equals(entries.length, 6, "There should be six entries: 4 scripts, 1 stylesheet, and the worker itself");
done();
}
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that resources requested by dedicated workers don't appear in the main document.</p>
</body>
</html>

View File

@ -0,0 +1,17 @@
importScripts("/resources/testharness.js");
async_test(function() {
const worker = new Worker('resources/worker_with_images.js');
worker.onmessage = this.step_func_done((event) => {
const childNumEntries = event.data;
assert_equals(2, childNumEntries,
"There should be two resource timing entries: 2 image XHRs");
const parentNumEntries = performance.getEntries().length;
assert_equals(2, parentNumEntries,
"There should be two resource timing entries: " +
"one is for importScripts() and the another is for a nested worker");
worker.terminate();
});
}, "Resource timing for nested dedicated workers");
done();

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Resource Timing reparenting elements</title>
<link rel="author" title="Google" href="http://www.google.com/" />
<link rel="help" href="http://www.w3.org/TR/resource-timing/#dom-performanceresourcetiming-initiatortype"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/webperftestharness.js"></script>
<script src="resources/webperftestharnessextension.js"></script>
<script>
let iframe;
function setup_iframe() {
iframe = document.getElementById('frameContext');
const d = iframe.contentWindow.document;
const iframeBody = d.createElement('body');
const move_to_parent = d.createElement('img');
move_to_parent.src = 'blue.png?id=move_to_parent';
iframeBody.appendChild(move_to_parent);
iframeBody.removeChild(move_to_parent);
const parentBody = document.getElementsByTagName('body')[0];
parentBody.appendChild(move_to_parent);
const move_to_child = document.createElement('img');
move_to_child.src = 'blue.png?id=move_to_child';
parentBody.appendChild(move_to_child);
parentBody.removeChild(move_to_child);
iframeBody.appendChild(move_to_child);
}
function onload_test() {
const context = new PerformanceContext(iframe.contentWindow.performance);
const entries = context.getEntriesByType('resource');
const index = window.location.pathname.lastIndexOf('/');
const pathname = window.location.pathname.substring(0, index);
let expected_entries = {};
expected_entries[pathname + '/resources/blue.png?id=move_to_child'] = 'img';
test_resource_entries(entries, expected_entries);
}
window.setup_iframe = setup_iframe;
</script>
</head>
<body>
<h1>Description</h1>
<p>This test validates that reparenting an element doesn't change the initiator document.</p>
<div id="log"></div>
<iframe id="frameContext" onload="onload_test();" src="resources/inject_resource_test.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
function numberOfDownloads(url) {
let absoluteURL = new URL(url, location.href).href;
return performance.getEntriesByName(absoluteURL).length;
}
function waitForSubFrameLoad() {
return new Promise((resolve) => {
window.subFrameLoaded = () => {
window.subFrameLoaded = null;
resolve();
};
});
}
function runTest(type) {
performance.clearResourceTimings();
let elem = document.createElement(type);
if (type === 'object')
elem.data = 'resources/self_navigation.html?' + type;
else
elem.src = 'resources/self_navigation.html?' + type;
document.body.appendChild(elem);
return waitForSubFrameLoad().then(() => {
let resources = performance.getEntriesByType('resource');
assert_equals(numberOfDownloads('resources/self_navigation.html?' + type), 1);
assert_equals(numberOfDownloads('resources/notify_parent.html?redirected'), 0);
document.body.removeChild(elem);
});
}
promise_test(
() => runTest('iframe'),
"Subsequent <iframe> navigations don't appear in the resource-timing buffer.");
promise_test(
() => runTest('frame'),
"Subsequent <frame> navigations don't appear in the resource-timing buffer.");
promise_test(
() => runTest('embed'),
"Subsequent <embed> navigations don't appear in the resource-timing buffer.");
promise_test(
() => runTest('object'),
"Subsequent <object> navigations don't appear in the resource-timing buffer.");
</script>
</body>

View File

@ -0,0 +1,64 @@
importScripts("/resources/testharness.js");
function check(initiatorType, protocol) {
let entries = performance.getEntries();
assert_equals(entries.length, 1);
assert_true(entries[0] instanceof PerformanceEntry);
assert_equals(entries[0].entryType, "resource");
assert_true(entries[0].startTime > 0);
assert_true(entries[0].duration > 0);
assert_true(entries[0] instanceof PerformanceResourceTiming);
assert_equals(entries[0].initiatorType, initiatorType);
assert_equals(entries[0].nextHopProtocol, protocol);
}
async_test(t => {
performance.clearResourceTimings();
// Fetch
fetch("resources/empty.js")
.then(r => r.blob())
.then(blob => {
check("fetch", "http/1.1");
})
// XMLHttpRequest
.then(() => {
return new Promise(resolve => {
performance.clearResourceTimings();
let xhr = new XMLHttpRequest();
xhr.onload = () => {
check("xmlhttprequest", "http/1.1");
resolve();
};
xhr.open("GET", "resources/empty.js");
xhr.send();
});
})
// Sync XMLHttpREquest
.then(() => {
performance.clearResourceTimings();
let xhr = new XMLHttpRequest();
xhr.open("GET", "resources/empty.js", false);
xhr.send();
check("xmlhttprequest", "http/1.1");
})
// ImportScripts
.then(() => {
performance.clearResourceTimings();
importScripts(["resources/empty.js"]);
check("other", "http/1.1");
})
// All done.
.then(() => {
t.done();
});
}, "Performance Resource Entries in workers");
done();

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>This test validates the value of encodedBodySize in certain situations.</title>
<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>
function test_resource_timing_for_content_length({actualContentLength, lengthHeader}, title) {
promise_test(async t => {
const content = new Array(actualContentLength).fill('x').join('')
const url = `resources/resource-timing-content-length.py?content=${content}&length=${lengthHeader}`
fetch(url)
const entry = await new Promise(resolve => new PerformanceObserver((entryList, observer) => {
observer.disconnect()
resolve(entryList.getEntries()[0])
}).observe({entryTypes: ['resource']}))
const expectedContentLength = Number.isInteger(lengthHeader) ? Math.min(actualContentLength, lengthHeader) : actualContentLength
assert_equals(entry.encodedBodySize, expectedContentLength)
}, title);
}
test_resource_timing_for_content_length({actualContentLength: 3, lengthHeader: 'auto'},
"encodedBodySize should be equal to the actual byte size of the content")
test_resource_timing_for_content_length({actualContentLength: 13, lengthHeader: 'none'},
"encodedBodySize should be equal to the actual byte size of the content when no header present")
test_resource_timing_for_content_length({actualContentLength: 7, lengthHeader: 3},
"encodedBodySize should be equal to the actual byte size of the content when header value is lower than actual content")
test_resource_timing_for_content_length({actualContentLength: 8, lengthHeader: 40},
"encodedBodySize should be equal to the actual byte size of the content when header value is higher than actual content")
</script>
</html>

View File

@ -0,0 +1,5 @@
HTTP/1.0 200 OK
Content-Length: 0
Timing-Allow-Origin: *

View File

@ -0,0 +1,3 @@
HTTP/1.0 200 OK
Content-Length: 0

View File

@ -0,0 +1,3 @@
HTTP/1.0 204 OK
Content-Length: 0

View File

@ -0,0 +1,3 @@
HTTP/1.0 205 OK
Content-Length: 0

View File

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Green Test Page</title>
</head>
<body style="background-color:#00FF00;">
<h1>Placeholder</h1>
</body>
</html>

View File

@ -0,0 +1 @@
Timing-Allow-Origin: *

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1 @@
Timing-Allow-Origin: *

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1,75 @@
// This script relies on resources/resource-loaders.js. Include it before in order for the below
// methods to work properly.
// The resources used to trigger new entries.
const scriptResources = [
'resources/empty.js',
'resources/empty_script.js',
'resources/empty.js?id'
];
const waitForNextTask = () => {
return new Promise(resolve => {
step_timeout(resolve, 0);
});
};
const clearBufferAndSetSize = size => {
performance.clearResourceTimings();
performance.setResourceTimingBufferSize(size);
}
const forceBufferFullEvent = async () => {
clearBufferAndSetSize(1);
return new Promise(async resolve => {
performance.addEventListener('resourcetimingbufferfull', resolve);
// Load 2 resources to ensure onresourcetimingbufferfull is fired.
// Load them in order in order to get the entries in that order!
await load.script(scriptResources[0]);
await load.script(scriptResources[1]);
});
};
const fillUpTheBufferWithSingleResource = async (src = scriptResources[0]) => {
clearBufferAndSetSize(1);
await load.script(src);
};
const fillUpTheBufferWithTwoResources = async () => {
clearBufferAndSetSize(2);
// Load them in order in order to get the entries in that order!
await load.script(scriptResources[0]);
await load.script(scriptResources[1]);
};
const addAssertUnreachedBufferFull = t => {
performance.addEventListener('resourcetimingbufferfull', t.step_func(() => {
assert_unreached("resourcetimingbufferfull should not fire")
}));
};
const checkEntries = numEntries => {
const entries = performance.getEntriesByType('resource');
assert_equals(entries.length, numEntries,
'Number of entries does not match the expected value.');
assert_true(entries[0].name.includes(scriptResources[0]),
scriptResources[0] + " is in the entries buffer");
if (entries.length > 1) {
assert_true(entries[1].name.includes(scriptResources[1]),
scriptResources[1] + " is in the entries buffer");
}
if (entries.length > 2) {
assert_true(entries[2].name.includes(scriptResources[2]),
scriptResources[2] + " is in the entries buffer");
}
}
const bufferFullFirePromise = new Promise(resolve => {
performance.addEventListener('resourcetimingbufferfull', async () => {
// Wait for the next task just to ensure that all bufferfull events have fired, and to ensure
// that the secondary buffer is copied (as this is an event, there may be microtask checkpoints
// right after running an event handler).
await waitForNextTask();
resolve();
});
});

View File

@ -0,0 +1 @@
<script>window.close()</script>

Some files were not shown because too many files have changed in this diff Show More