Test prerender/service-worker interactions (#32710)

diff --git a/speculation-rules/prerender/cookies.html b/speculation-rules/prerender/cookies.html
index 7f5e416..bdae3c5 100644
--- a/speculation-rules/prerender/cookies.html
+++ b/speculation-rules/prerender/cookies.html
@@ -19,7 +19,6 @@
   document.cookie = initiator_cookie;
   const result = await exec(() => {
     const result = document.cookie;
-    prerender_log(result);
     document.cookie = "prerender_cookie=exist;path=/;";
     return result;
   });
diff --git a/speculation-rules/prerender/media-autoplay.html b/speculation-rules/prerender/media-autoplay.html
index efbff14..7e6b8d1 100644
--- a/speculation-rules/prerender/media-autoplay.html
+++ b/speculation-rules/prerender/media-autoplay.html
@@ -18,7 +18,6 @@
       video.autoplay = true;
       video.muted = true;
       window.video = video;
-      prerender_log('hello2');
       document.body.appendChild(video);
     });
 
diff --git a/speculation-rules/prerender/resources/service-worker.js b/speculation-rules/prerender/resources/service-worker.js
new file mode 100644
index 0000000..763d557
--- /dev/null
+++ b/speculation-rules/prerender/resources/service-worker.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", async e => {
+    if (e.request.url.endsWith("ping"))
+        e.respondWith(new Response('pong'));
+    else if (e.request.url.endsWith("client")) {
+        e.respondWith((async () => {
+            const client = await clients.get(e.clientId);
+            const clientInfo = client ? {id: e.clientId, visibilityState: client.visibilityState, focused: client.focused} : null;
+            return new Response(JSON.stringify({clientInfo}), {headers: {'Content-Type': 'application/json'}});
+        })());
+    }
+});
diff --git a/speculation-rules/prerender/service-workers.https.html b/speculation-rules/prerender/service-workers.https.html
new file mode 100644
index 0000000..5187e30
--- /dev/null
+++ b/speculation-rules/prerender/service-workers.https.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<title>Same-origin prerendering can access Indexed Database</title>
+<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="resources/utils.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<body>
+<script>
+
+setup(() => assertSpeculationRulesIsSupported());
+
+// This delay is to prevent a race condition which can cause false passes -
+// a service worker might take some time to install, and if activation is too quick it might
+// end up occuring after activation by chance.
+const ACTIVATION_DELAY = 500;
+
+promise_test(async t => {
+  const reg = await service_worker_unregister_and_register(
+    t, "./resources/service-worker.js", `resources/`);
+
+  t.add_cleanup(() => reg.unregister());
+  const {exec} = await create_prerendered_page(t);
+  const {text, prerendering} = await exec(async () => {
+    const text = await (await fetch(`ping`)).text();
+    return {text, prerendering: document.prerendering};
+  });
+
+  assert_true(prerendering);
+  assert_equals(text, 'pong');
+}, 'A prerendered page should be able to access an existing Service Worker');
+
+promise_test(async t => {
+  const {exec, activate} = await create_prerendered_page(t);
+  const scope = `./${token()}/`;
+  await exec(async scope => {
+    window.serviceWorkerInstalled = new Promise(resolve => {
+      navigator.serviceWorker.register('./service-worker.js', {scope})
+        .then(reg => {
+          reg.unregister();
+          resolve({prerendering: document.prerendering});
+        });
+    });
+  }, scope);
+
+  await new Promise(resolve => t.step_timeout(resolve, ACTIVATION_DELAY));
+
+  await activate();
+
+  const {prerendering} = await exec(async () => { return await window.serviceWorkerInstalled});
+  assert_false(prerendering, 'Service Worker Installation should occur after activation');
+}, 'Registering a new service worker from a prerendered page should be delayed');
+
+promise_test(async t => {
+  const uid = token();
+  const reg = await service_worker_unregister_and_register(
+    t, "./resources/service-worker.js", `./resources/${uid}/`);
+  t.add_cleanup(() => reg.unregister());
+
+  const {exec, activate} = await create_prerendered_page(t);
+  await exec(async uid => {
+    window.serviceWorkerUnregistered = (async () => {
+      const regs = await navigator.serviceWorker.getRegistrations();
+      const reg = regs.find(r => r.scope.includes(uid));
+      await reg.unregister();
+      return {prerendering: document.prerendering};
+    })();
+  }, uid);
+
+  await new Promise(resolve => t.step_timeout(resolve, ACTIVATION_DELAY));
+
+  await activate();
+
+  const {prerendering} = await exec(() => window.serviceWorkerUnregistered);
+  assert_false(prerendering, 'Service Worker deregistration should occur after activation');
+}, 'Unregistering an exsiting service worker from a prerendered page should be delayed');
+
+promise_test(async t => {
+  const uid = token();
+  const reg = await service_worker_unregister_and_register(
+    t, "./resources/service-worker.js", `./resources/${uid}/`);
+  t.add_cleanup(() => reg.unregister());
+
+  const {exec, activate} = await create_prerendered_page(t);
+  await exec(async uid => {
+    window.serviceWorkerUpdated = (async () => {
+      const regs = await navigator.serviceWorker.getRegistrations();
+      const reg = regs.find(r => r.scope.includes(uid));
+      await reg.update();
+      return {prerendering: document.prerendering};
+    })();
+  }, uid);
+
+  await new Promise(resolve => t.step_timeout(resolve, ACTIVATION_DELAY));
+
+  await activate();
+
+  const {prerendering} = await exec(() => window.serviceWorkerUpdated);
+  assert_false(prerendering, 'Service Worker updates should occur after activation');
+}, 'Updating an exsiting service worker from a prerendered page should be delayed');
+
+promise_test(async t => {
+  const reg = await service_worker_unregister_and_register(
+    t, "./resources/service-worker.js", 'resources/');
+
+  t.add_cleanup(() => reg.unregister());
+  const {exec} = await create_prerendered_page(t);
+  const {clientInfo} = await exec(async () => (await fetch(`client`)).json());
+  assert_not_equals(clientInfo.id, null);
+  assert_equals(clientInfo.visibilityState, 'hidden');
+  assert_equals(clientInfo.focused, false);
+}, 'A prerendered page should be accessible as a hidden & unfocused SW client');
+
+</script>
+</body>