Tentative test for memory cache behavior
diff --git a/preload/resources/echo-with-cors.py b/preload/resources/echo-with-cors.py
index 06d30c3..a79ca00 100644
--- a/preload/resources/echo-with-cors.py
+++ b/preload/resources/echo-with-cors.py
@@ -1,8 +1,13 @@
def main(request, response):
response.headers.set(b"Content-Type", request.GET.first(b"type"))
origin = request.headers.get('Origin')
+ cache = request.GET.first(b'cache', None)
+
if origin is not None:
response.headers.set(b"Access-Control-Allow-Origin", origin)
response.headers.set(b"Access-Control-Allow-Credentials", b"true")
+
+ if cache is not None:
+ response.headers.set(b"Cache-Control", cache)
return request.GET.first(b"content")
diff --git a/resource-timing/memory-cache-behavior.tentative.html b/resource-timing/memory-cache-behavior.tentative.html
new file mode 100644
index 0000000..71ea252
--- /dev/null
+++ b/resource-timing/memory-cache-behavior.tentative.html
@@ -0,0 +1,172 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body></body>
+<script>
+
+const {HTTPS_REMOTE_ORIGIN} = get_host_info();
+
+function createEchoURL(text, type) {
+ return `/preload/resources/echo-with-cors.py?type=${
+ encodeURIComponent(type)}&content=${
+ encodeURIComponent(text)}`
+}
+
+const urls = {
+ image: createEchoURL('<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2" />', 'image/svg+xml'),
+ font: '/preload/resources/font.ttf?x',
+ script: createEchoURL('function dummy() { }', 'application/javascript'),
+ style: createEchoURL('.cls { }', 'text/css'),
+}
+
+const resourceTypes = {
+ image: {url: urls.image, as: 'image'},
+ font: {url: urls.font, as: 'font', config: 'anonymous'},
+ script: {url: urls.script, as: 'script'},
+ style: {url: urls.style, as: 'style'}
+}
+
+const configs = {
+ // The requested URL is from the same origin
+ 'same-origin': {crossOrigin: false, attributes: {}},
+
+ // The requested URL is from a remote origin, without CORS
+ 'no-cors': {crossOrigin: true, attributes: {}},
+
+ // The requested URL is from a remote origin, with CORS (anonymous)
+ 'anonymous': {crossOrigin: true, attributes: {crossOrigin: 'anonymous'}},
+
+ // The requested URL is from a remote origin, with CORS (including credentials)
+ 'use-credentials': {crossOrigin: true, attributes: {crossOrigin: 'use-credentials'}},
+}
+
+const loaders = {
+ image: async (href, attr) => {
+ const img = document.createElement('img');
+ Object.entries(attr).forEach(([key, value]) => {
+ img[key] = value;
+ });
+
+ img.src = href
+
+ document.body.appendChild(img);
+ await new Promise(resolve => {
+ img.addEventListener('load', resolve);
+ img.addEventListener('error', resolve);
+ });
+
+ return () => img.remove();
+ },
+ font: async (href, attr) => {
+ const style = document.createElement('style');
+ style.innerHTML = `@font-face {
+ font-family: 'MyFont';
+ src: url('${href}');
+ }`;
+
+ document.head.appendChild(style);
+ const p = document.createElement('p');
+ p.innerText = 'abc';
+ p.style.fontFamily = 'MyFont';
+ document.body.appendChild(p);
+ await document.fonts.ready;
+ return () => {
+ p.remove();
+ style.remove();
+ };
+ },
+ shape: (href, attr) => {
+ const div = document.createElement('div');
+ div.style.shapeOutside = `url(${href})`;
+ document.body.appendChild(div);
+ return () => div.remove();
+ },
+ backgroundImage: (href, attr) => {
+ const div = document.createElement('div');
+ div.style.background = `url(${href})`;
+ document.body.appendChild(div);
+ return () => div.remove();
+ },
+ script: async (href, attr) => {
+ const script = document.createElement('script');
+ if (attr.crossOrigin)
+ script.setAttribute('crossorigin', attr.crossOrigin);
+ script.src = href;
+ document.body.appendChild(script);
+ await new Promise(resolve => { script.onload = resolve });
+ return () => script.remove();
+ },
+ module: async (href, attr) => {
+ const script = document.createElement('script');
+ script.type = 'module';
+ const cleanup = () => script.remove();
+ if (attr.crossOrigin)
+ script.setAttribute('crossorigin', attr.crossOrigin);
+ script.src = href;
+ document.body.appendChild(script);
+ await new Promise(resolve => { script.onload = resolve });
+ return cleanup;
+ },
+ style: async (href, attr) => {
+ const style = document.createElement('link');
+ style.rel = 'stylesheet';
+ const cleanup = () => style.remove();
+ style.href = href;
+ if (attr.crossOrigin)
+ style.setAttribute('crossorigin', attr.crossOrigin);
+ document.head.appendChild(style);
+ await new Promise(resolve => {
+ style.addEventListener('load', resolve);
+ style.addEventListener('error', resolve);
+ });
+
+ return cleanup;
+ }
+}
+
+function memory_cache_reuse_test(type, cacheType, deadOrAlive, as, url, cfg1, cfg2) {
+ const expected = (cfg1 === cfg2 && cacheType !== 'no-store') ? "reuse" : "discard";
+ const key = token();
+ let href = getAbsoluteURL(`${
+ (configs[cfg2].crossOrigin ? HTTPS_REMOTE_ORIGIN : '') + url
+ }&${token()}`);
+
+ if (cacheType !== 'none')
+ href += `&cache=${encodeURIComponent(cacheType)}`
+ promise_test(async t => {
+ const remove = await loaders[as](href, configs[cfg1].attributes);
+ if (deadOrAlive === 'dead')
+ await remove();
+ else
+ t.add_cleanup(remove);
+// await new Promise(resolve => t.step_timeout(resolve, 100));
+ const remove2 = await loaders[as](href, configs[cfg2].attributes);
+ t.add_cleanup(remove2);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ verifyNumberOfResourceTimingEntries(href, expected === "reuse" ? 1 : 2)
+ }, `Loading ${type} (${cfg1} and then ${cfg2}, ${deadOrAlive}) with cache config ${cacheType} should ${expected} the first response`);
+}
+
+for (const [resourceTypeName, resourceInfo] of Object.entries(resourceTypes)) {
+ const configNames = resourceInfo.config ? [resourceInfo.config, 'same-origin'] : Object.keys(configs)
+ for (const cfg2Name of configNames) {
+ for (const cfg1Name of Object.keys(configs)) {
+ for (const cacheType of ['none', 'max-age=0', 'no-store']) {
+ for (deadOrAlive of ['alive']) {
+ // Same-origin requests ignore their CORS attributes, so no need to match all of them.
+ if ((cfg2Name === 'same-origin' && cfg1Name === 'same-origin') ||
+ (cfg2Name !== 'same-origin' && cfg1Name !== 'same-origin') &&
+ (!resourceInfo.config || (resourceInfo.config === cfg1Name && resourceInfo.config === cfg2Name))) {
+ memory_cache_reuse_test(resourceTypeName, cacheType, deadOrAlive, resourceInfo.as, resourceInfo.url, cfg1Name, cfg2Name);
+ }
+
+ }
+ }
+ }
+ }
+}
+</script>
\ No newline at end of file