diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/fetch/stale-while-revalidate | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
10 files changed, 445 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/fetch-sw.https.html b/testing/web-platform/tests/fetch/stale-while-revalidate/fetch-sw.https.html new file mode 100644 index 0000000000..efcebc24a6 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/fetch-sw.https.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Stale Revalidation Requests don't get sent to service worker</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="../../service-workers/service-worker/resources/test-helpers.sub.js"></script> + <script src="/common/utils.js"></script> +</head> +<body> +<script> + + // Duplicating this resource to make service worker scoping simpler. + async function setupRegistrationAndWaitToBeControlled(t, scope) { + const controlled = new Promise((resolve) => { + navigator.serviceWorker.oncontrollerchange = () => { resolve(); }; + }); + const reg = await navigator.serviceWorker.register('sw-intercept.js'); + await wait_for_state(t, reg.installing, 'activated'); + await controlled; + add_completion_callback(_ => reg.unregister()); + return reg; + } + + // Using 250ms polling interval to provide enough 'network calmness' to give + // the background low priority revalidation request a chance to kick in. + function wait250ms(test) { + return new Promise(resolve => { + test.step_timeout(() => { + resolve(); + }, 250); + }); + } + + promise_test(async (test) => { + var request_token = token(); + const uri = 'resources/stale-script.py?token=' + request_token; + + await setupRegistrationAndWaitToBeControlled(test, 'resources/stale-script.py'); + + var service_worker_count = 0; + navigator.serviceWorker.addEventListener('message', function once(event) { + if (event.data.endsWith(uri)) { + service_worker_count++; + } + }); + + const response = await fetch(uri); + const response2 = await fetch(uri); + assert_equals(response.headers.get('Unique-Id'), response2.headers.get('Unique-Id')); + while(true) { + const revalidation_check = await fetch(`resources/stale-script.py?query&token=` + request_token); + if (revalidation_check.headers.get('Count') == '2') { + // The service worker should not see the revalidation request. + assert_equals(service_worker_count, 2); + break; + } + await wait250ms(test); + } + }, 'Second fetch returns same response'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/fetch.any.js b/testing/web-platform/tests/fetch/stale-while-revalidate/fetch.any.js new file mode 100644 index 0000000000..3682b9d2c3 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/fetch.any.js @@ -0,0 +1,32 @@ +// META: global=window,worker +// META: title=Tests Stale While Revalidate is executed for fetch API +// META: script=/common/utils.js + +function wait25ms(test) { + return new Promise(resolve => { + test.step_timeout(() => { + resolve(); + }, 25); + }); +} + +promise_test(async (test) => { + var request_token = token(); + + const response = await fetch(`resources/stale-script.py?token=` + request_token); + // Wait until resource is completely fetched to allow caching before next fetch. + const body = await response.text(); + const response2 = await fetch(`resources/stale-script.py?token=` + request_token); + + assert_equals(response.headers.get('Unique-Id'), response2.headers.get('Unique-Id')); + const body2 = await response2.text(); + assert_equals(body, body2); + + while(true) { + const revalidation_check = await fetch(`resources/stale-script.py?query&token=` + request_token); + if (revalidation_check.headers.get('Count') == '2') { + break; + } + await wait25ms(test); + } +}, 'Second fetch returns same response'); diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-css.py b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-css.py new file mode 100644 index 0000000000..b87668373a --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-css.py @@ -0,0 +1,28 @@ +def main(request, response): + + token = request.GET.first(b"token", None) + is_query = request.GET.first(b"query", None) != None + with request.server.stash.lock: + value = request.server.stash.take(token) + count = 0 + if value != None: + count = int(value) + if is_query: + if count < 2: + request.server.stash.put(token, count) + else: + count = count + 1 + request.server.stash.put(token, count) + if is_query: + headers = [(b"Count", count)] + content = b"" + return 200, headers, content + else: + content = b"body { background: rgb(0, 128, 0); }" + if count > 1: + content = b"body { background: rgb(255, 0, 0); }" + + headers = [(b"Content-Type", b"text/css"), + (b"Cache-Control", b"private, max-age=0, stale-while-revalidate=60")] + + return 200, headers, content diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-image.py b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-image.py new file mode 100644 index 0000000000..36e6fc0c9b --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-image.py @@ -0,0 +1,40 @@ +import os.path + +from wptserve.utils import isomorphic_decode + +def main(request, response): + + token = request.GET.first(b"token", None) + is_query = request.GET.first(b"query", None) != None + with request.server.stash.lock: + value = request.server.stash.take(token) + count = 0 + if value != None: + count = int(value) + if is_query: + if count < 2: + request.server.stash.put(token, count) + else: + count = count + 1 + request.server.stash.put(token, count) + + if is_query: + headers = [(b"Count", count)] + content = b"" + return 200, headers, content + else: + filename = u"green-16x16.png" + if count > 1: + filename = u"green-256x256.png" + + path = os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"../../../images", filename) + body = open(path, "rb").read() + + response.add_required_headers = False + response.writer.write_status(200) + response.writer.write_header(b"content-length", len(body)) + response.writer.write_header(b"Cache-Control", b"private, max-age=0, stale-while-revalidate=60") + response.writer.write_header(b"content-type", b"image/png") + response.writer.end_headers() + + response.writer.write(body) diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-script.py b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-script.py new file mode 100644 index 0000000000..731cd80565 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/resources/stale-script.py @@ -0,0 +1,32 @@ +import random, string + +def id_token(): + letters = string.ascii_lowercase + return b''.join(random.choice(letters).encode("utf-8") for i in range(20)) + +def main(request, response): + token = request.GET.first(b"token", None) + is_query = request.GET.first(b"query", None) != None + with request.server.stash.lock: + value = request.server.stash.take(token) + count = 0 + if value != None: + count = int(value) + if is_query: + if count < 2: + request.server.stash.put(token, count) + else: + count = count + 1 + request.server.stash.put(token, count) + + if is_query: + headers = [(b"Count", count)] + content = u"" + return 200, headers, content + else: + unique_id = id_token() + headers = [(b"Content-Type", b"text/javascript"), + (b"Cache-Control", b"private, max-age=0, stale-while-revalidate=60"), + (b"Unique-Id", unique_id)] + content = b"report('%s')" % unique_id + return 200, headers, content diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/revalidate-not-blocked-by-csp.html b/testing/web-platform/tests/fetch/stale-while-revalidate/revalidate-not-blocked-by-csp.html new file mode 100644 index 0000000000..ea70b9a9c7 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/revalidate-not-blocked-by-csp.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test revalidations requests aren't blocked by CSP.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<script> + +// Regression test for https://crbug.com/1070117. +var request_token = token(); +let image_src = "resources/stale-image.py?token=" + request_token; + +let loadImage = async () => { + let img = document.createElement("img"); + img.src = image_src; + let loaded = new Promise(r => img.onload = r); + document.body.appendChild(img); + await loaded; + return img; +}; + +promise_test(async t => { + await new Promise(r => window.onload = r); + + // No CSP report must be sent from now. + // + // TODO(arthursonzogni): Some browser implementations do not support the + // ReportingObserver yet. Ideally, another way to access the reports should be + // used to test them. + const observer = new ReportingObserver(t.unreached_func( + "CSP reports aren't sent for revalidation requests")); + if (observer) + observer.observe(); + + let img1 = await loadImage(); // Load initial resource. + let img2 = loadImage(); // Request stale resource. + + // Insert a <meta> CSP. This will block any image load starting from now. + const metaCSP = document.createElement("meta"); + metaCSP.httpEquiv = "Content-Security-Policy"; + metaCSP.content = "img-src 'none'"; + document.getElementsByTagName("head")[0].appendChild(metaCSP) + + // The images were requested before the <meta> CSP above was added. So they + // will load. Nevertheless, the resource will be stale. A revalidation request + // is going to be made after that. + assert_equals(img1.width, 16, "(initial version loaded)"); + assert_equals((await img2).width, 16, "(stale version loaded)"); + + // At some point, the <img> resource is going to be revalidated. It must not + // be blocked nor trigger a CSP violation report. + + // Query the server again and again. At some point it must have received the + // revalidation request. We poll, because we don't know when the revalidation + // will occur. + let query = false; + while(true) { + await new Promise(r => step_timeout(r, 25)); + let response = await fetch(`${image_src}${query ? "&query" : ""}`); + let count = response.headers.get("Count"); + if (count == "2") + break; + query ^= true; + } +}, "Request revalidation aren't blocked by CSP"); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/stale-css.html b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-css.html new file mode 100644 index 0000000000..603a60c8bb --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-css.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Tests Stale While Revalidate works for css</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<script> + +var request_token = token(); +let link_src = "./resources/stale-css.py?token=" + request_token; + +let loadLink = async() => { + let link = document.createElement("link"); + link.rel = "stylesheet"; + link.type = "text/css"; + link.href = "resources/stale-css.py?token=" + request_token; + let loaded = new Promise(r => link.onload = r); + document.body.appendChild(link); + await loaded; + return window + .getComputedStyle(document.body) + .getPropertyValue('background-color'); +}; + +promise_test(async t => { + await new Promise(r => window.onload = r); + + let bgColor1 = await loadLink(); + assert_equals(bgColor1, "rgb(0, 128, 0)", "(initial version loaded)"); + + let bgColor2 = await loadLink(); + assert_equals(bgColor2, "rgb(0, 128, 0)", "(stale version loaded)"); + + // Query the server again and again. At some point it must have received the + // revalidation request. We poll, because we don't know when the revalidation + // will occur. + while(true) { + await new Promise(r => step_timeout(r, 25)); + let response = await fetch(link_src + "&query"); + let count = response.headers.get("Count"); + if (count == '2') + break; + } + + let bgColor3 = await loadLink(); + assert_equals(bgColor3, "rgb(255, 0, 0)", "(revalidated version loaded)"); +}, 'Cache returns stale resource'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/stale-image.html b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-image.html new file mode 100644 index 0000000000..d86bdfbde2 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-image.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Tests Stale While Revalidate works for images</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<!-- +Use a child document to load the second stale image into because +an image loaded into the same document will skip cache-control headers. +See: https://html.spec.whatwg.org/#the-list-of-available-images +--> +<iframe id="child1" srcdoc=""></iframe> +<iframe id="child2" srcdoc=""></iframe> +<script> + +var request_token = token(); +let image_src = "resources/stale-image.py?token=" + request_token; + +let loadImage = async (document) => { + let img = document.createElement("img"); + img.src = image_src; + let loaded = new Promise(r => img.onload = r); + document.body.appendChild(img); + await loaded; + return img; +}; + +promise_test(async t => { + await new Promise(r => window.onload = r); + + let img1 = await loadImage(document); + assert_equals(img1.width, 16, "(initial version loaded)"); + + let img2 = await loadImage(child1.contentDocument); + assert_equals(img2.width, 16, "(stale version loaded)"); + + // Query the server again and again. At some point it must have received the + // revalidation request. We poll, because we don't know when the revalidation + // will occur. + while(true) { + await new Promise(r => step_timeout(r, 25)); + let response = await fetch(image_src + "&query"); + let count = response.headers.get("Count"); + if (count == '2') + break; + } + + let img3 = await loadImage(child2.contentDocument); + assert_equals(img3.width, 256, "(revalidated version loaded)"); + +}, 'Cache returns stale resource'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/stale-script.html b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-script.html new file mode 100644 index 0000000000..f5317482c4 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/stale-script.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Tests Stale While Revalidate works for scripts</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<script> + +const request_token = token(); +const script_src = "./resources/stale-script.py?token=" + request_token; + +// The script above will call report() via a uniquely generated ID on the +// subresource. If it is a cache hit, the ID will be the same and +// |last_modified_count| won't be incremented. +let last_modified; +let last_modified_count = 0; +function report(mod) { + if (last_modified == mod) + return; + last_modified = mod; + last_modified_count++; +} + +let loadScript = async () => { + let script = document.createElement("script"); + let script_loaded = new Promise(r => script.onload = r); + script.src = script_src; + document.body.appendChild(script); + await script_loaded; +}; + +promise_test(async t => { + await new Promise(r => window.onload = r); + + await loadScript(); + assert_equals(last_modified_count, 1, '(initial version loaded)'); + + await loadScript(); + assert_equals(last_modified_count, 1, '(stale version loaded)'); + + // Query the server again and again. At some point it must have received the + // revalidation request. We poll, because we don't know when the revalidation + // will occur. + while(true) { + await new Promise(r => step_timeout(r, 25)); + let response = await fetch(script_src + "&query"); + let count = response.headers.get("Count"); + if (count == '2') + break; + } + + await loadScript(); + assert_equals(last_modified_count, 2, '(revalidated version loaded)'); + +}, 'Cache returns stale resource'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/stale-while-revalidate/sw-intercept.js b/testing/web-platform/tests/fetch/stale-while-revalidate/sw-intercept.js new file mode 100644 index 0000000000..dca7de51b0 --- /dev/null +++ b/testing/web-platform/tests/fetch/stale-while-revalidate/sw-intercept.js @@ -0,0 +1,14 @@ +async function broadcast(msg) { + for (const client of await clients.matchAll()) { + client.postMessage(msg); + } +} + +self.addEventListener('fetch', event => { + event.waitUntil(broadcast(event.request.url)); + event.respondWith(fetch(event.request)); +}); + +self.addEventListener('activate', event => { + self.clients.claim(); +}); |