Add more tests for dangling markup mitigation

Adding more test per request[1].

[1] https://github.com/whatwg/html/pull/10022#pullrequestreview-1841928335

Change-Id: I7d4d3494fa3aa0ac41c48727c2f866ccf3f016d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5278680
Reviewed-by: Jonathan Hao <phao@chromium.org>
Reviewed-by: Yifan Luo <lyf@chromium.org>
Auto-Submit: Jun Kokatsu <jkokatsu@google.com>
Commit-Queue: Jun Kokatsu <jkokatsu@google.com>
Cr-Commit-Position: refs/heads/main@{#1261160}
diff --git a/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html b/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html
new file mode 100644
index 0000000..66456a8
--- /dev/null
+++ b/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+  const blank = 'about:blank';
+  const dangling_url = 'resources/empty.html?\n<';
+  const api_calls = [
+    `window.open(\`${dangling_url}\`,'_self')`,
+    `location.replace(\`${dangling_url}\`)`,
+  ];
+
+  api_calls.forEach(call => {
+    async_test(t => {
+      const iframe =
+        document.body.appendChild(document.createElement('iframe'));
+      t.step(() => {
+        iframe.contentWindow.eval(call)
+        t.step_timeout(()=>{
+          assert_false(iframe.contentWindow.location.href.endsWith(blank));
+          t.done();
+        }, 500);
+      });
+    }, `Does not block ${call}`);
+  });
+</script>
diff --git a/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html b/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html
similarity index 100%
rename from fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html
rename to fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html
diff --git a/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html b/fetch/security/dangling-markup/dangling-markup-mitigation.html
similarity index 100%
rename from fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html
rename to fetch/security/dangling-markup/dangling-markup-mitigation.html
diff --git a/fetch/security/dangling-markup/dangling-markup-mitigation.https.html b/fetch/security/dangling-markup/dangling-markup-mitigation.https.html
new file mode 100644
index 0000000..3f038cb
--- /dev/null
+++ b/fetch/security/dangling-markup/dangling-markup-mitigation.https.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+  function get_requests(worker, expected) {
+    return new Promise(resolve => {
+      navigator.serviceWorker.addEventListener('message', function onMsg(evt) {
+        if (evt.data.size >= expected) {
+          navigator.serviceWorker.removeEventListener('message', onMsg);
+          resolve(evt.data);
+        } else {
+          worker.postMessage("");
+        }
+      });
+      worker.postMessage("");
+    });
+  }
+
+  const resources = [
+    x=>`<link rel="stylesheet" href="404/style?${x}">`,
+    x=>`<link rel="prefetch" as="style" href="404/prefetch?${x}">`,
+    x=>`<script src="404/script?${x}"><\/script>`,
+    x=>`<iframe src="404/iframe?${x}"></iframe>`,
+    x=>`<meta http-equiv="refresh" content="0;url=404/meta?${x}">`,
+    x=>`<a href="404/a?${x}">click</a><script>document.querySelector('a').click()<\/script>`,
+    x=>`<base href="404/base?${x}"><a href>me</a><script>document.querySelector('a').click()<\/script>`,
+    x=>`<video controls poster="404/poster?${x}"></video>`,
+    x=>`<input type="image" src="404/input?${x}">`,
+    x=>`<form method="GET" action="404/form?${x}"></form><script>document.querySelector('form').submit()<\/script>`,
+    x=>`<body background="404/body?${x}"></body>`,
+  ];
+
+  async_test(t => {
+    const script = 'service-worker.js';
+    const paths = [];
+    navigator.serviceWorker.register(script);
+    t.step(async () => {
+      const registration = await navigator.serviceWorker.ready;
+      for (const html of resources) {
+        const iframe1 =
+          document.body.appendChild(document.createElement('iframe'));
+        iframe1.src = 'resources.html?html=' + html`%0A<`;
+        const iframe2 =
+          document.body.appendChild(document.createElement('iframe'));
+        iframe2.src = 'resources.html?html=' + html``;
+        const path = html`EOP`;
+        paths.push(path.substring(path.search('404\\/')+4, path.search('EOP')));
+      }
+
+      const requests = await get_requests(registration.active, resources.length);
+      paths.forEach(path => {
+        assert_true(requests.has(path),
+                    `${path} should appear in requests sent`);
+      });
+      await registration.unregister();
+      t.done();
+    });
+  }, 'Only blocks dangling markup requests');
+</script>
diff --git a/fetch/security/dangling-markup/resources/empty.html b/fetch/security/dangling-markup/resources/empty.html
new file mode 100644
index 0000000..0e76edd
--- /dev/null
+++ b/fetch/security/dangling-markup/resources/empty.html
@@ -0,0 +1 @@
+<!DOCTYPE html>
diff --git a/fetch/security/dangling-markup/service-worker.js b/fetch/security/dangling-markup/service-worker.js
new file mode 100644
index 0000000..837e216
--- /dev/null
+++ b/fetch/security/dangling-markup/service-worker.js
@@ -0,0 +1,35 @@
+const requests = new Set();
+
+addEventListener('install', evt => {
+  evt.waitUntil(self.skipWaiting());
+});
+
+addEventListener('activate', evt => {
+  evt.waitUntil(self.clients.claim());
+});
+
+addEventListener('message', evt => {
+  evt.source.postMessage(requests);
+});
+
+addEventListener('fetch', evt => {
+  const url = new URL(evt.request.url);
+  const path = url.pathname;
+  const search = url.search || "?";
+  if (path.includes('404')) {
+    const dir = path.split('/');
+    const request = dir[dir.length-1] + search;
+    if (!requests.has(request)) {
+      requests.add(request);
+    }
+    evt.respondWith(new Response(""));
+  } else if (path.endsWith('resources.html')) {
+    const html = (new URLSearchParams(search)).get('html');
+    evt.respondWith(new Response(html, {
+      headers: {
+        "Content-Type": "text/html"
+      }
+    }));
+  }
+  return;
+});