[Credentialless] WPT <iframe>

Add one WPT test about <iframe>.

A few parameters can vary:
- Parent is COEP:{none,credentialless,require-corp}
- Child is COEP:{none,credentialless,require-corp}
- Child is CORP:{undefined, cross-origin}
- Child is {same-origin,cross-origin} with its parent.

Depending on all of those, the iframe must be blocked or must loaded.

Bug: 1175099
Change-Id: I6f60cac9211afdecf9969050efb5e4598cc1052e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2709861
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Camille Lamy <clamy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#857616}
diff --git a/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.tentative.https.html b/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.tentative.https.html
new file mode 100644
index 0000000..85d2be9
--- /dev/null
+++ b/html/cross-origin-embedder-policy/credentialless/iframe-coep-credentialless.tentative.https.html
@@ -0,0 +1,39 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/common.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script src="./resources/iframeTest.js"></script>
+
+<script>
+
+const parent_coep_credentialless = newWindow(coep_credentialless);
+iframeTest("COEP:credentialless embeds same-origin COEP:none",
+  parent_coep_credentialless, same_origin, coep_none, EXPECT_BLOCK);
+iframeTest("COEP:credentialless embeds cross-origin COEP:none",
+  parent_coep_credentialless, cross_origin, coep_none, EXPECT_BLOCK);
+iframeTest("COEP:credentialless embeds same-origin COEP:credentialless",
+  parent_coep_credentialless, same_origin, coep_credentialless, EXPECT_LOAD);
+iframeTest("COEP:credentialless embeds cross-origin COEP:credentialless",
+  parent_coep_credentialless, cross_origin, coep_credentialless, EXPECT_BLOCK);
+iframeTest("COEP:credentialless embeds same-origin COEP:require-corp",
+  parent_coep_credentialless, same_origin, coep_require_corp, EXPECT_LOAD);
+iframeTest("COEP:credentialless embeds cross-origin COEP:require-corp",
+  parent_coep_credentialless, cross_origin, coep_require_corp, EXPECT_BLOCK);
+
+// Using CORP:cross-origin might unblock previously blocked iframes.
+iframeTestCORP("COEP:credentialless embeds same-origin COEP:none",
+  parent_coep_credentialless, same_origin, coep_none, EXPECT_BLOCK);
+iframeTestCORP("COEP:credentialless embeds cross-origin COEP:none",
+  parent_coep_credentialless, cross_origin, coep_none, EXPECT_BLOCK);
+iframeTestCORP("COEP:credentialless embeds same-origin COEP:credentialless",
+  parent_coep_credentialless, same_origin, coep_credentialless, EXPECT_LOAD);
+iframeTestCORP("COEP:credentialless embeds cross-origin COEP:credentialless",
+  parent_coep_credentialless, cross_origin, coep_credentialless, EXPECT_LOAD);
+iframeTestCORP("COEP:credentialless embeds same-origin COEP:require-corp",
+  parent_coep_credentialless, same_origin, coep_require_corp, EXPECT_LOAD);
+iframeTestCORP("COEP:credentialless embeds cross-origin COEP:require-corp",
+  parent_coep_credentialless, cross_origin, coep_require_corp, EXPECT_LOAD);
+
+</script>
diff --git a/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.tentative.https.html b/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.tentative.https.html
new file mode 100644
index 0000000..ad28c3a
--- /dev/null
+++ b/html/cross-origin-embedder-policy/credentialless/iframe-coep-none.tentative.https.html
@@ -0,0 +1,25 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/common.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script src="./resources/iframeTest.js"></script>
+
+<script>
+
+const parent_coep_none = newWindow(coep_none);
+iframeTest("COEP:none embeds same-origin COEP:none",
+  parent_coep_none, same_origin, coep_none, EXPECT_LOAD);
+iframeTest("COEP:none embeds cross-origin COEP:none",
+  parent_coep_none, cross_origin, coep_none, EXPECT_LOAD);
+iframeTest("COEP:none embeds same-origin COEP:credentialless",
+  parent_coep_none, same_origin, coep_credentialless, EXPECT_LOAD);
+iframeTest("COEP:none embeds cross-origin COEP:credentialless",
+  parent_coep_none, cross_origin, coep_credentialless, EXPECT_LOAD);
+iframeTest("COEP:none embeds same-origin COEP:require-corp",
+  parent_coep_none, same_origin, coep_require_corp, EXPECT_LOAD);
+iframeTest("COEP:none embeds cross-origin COEP:require-corp",
+  parent_coep_none, cross_origin, coep_require_corp, EXPECT_LOAD);
+
+</script>
diff --git a/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.tentative.https.html b/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.tentative.https.html
new file mode 100644
index 0000000..cb86ff2
--- /dev/null
+++ b/html/cross-origin-embedder-policy/credentialless/iframe-coep-require-corp.tentative.https.html
@@ -0,0 +1,39 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/common.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script src="./resources/iframeTest.js"></script>
+
+<script>
+
+const parent_coep_require_corp = newWindow(coep_require_corp);
+iframeTest("COEP:require-corp embeds same-origin COEP:none",
+  parent_coep_require_corp, same_origin, coep_none, EXPECT_BLOCK);
+iframeTest("COEP:require-corp embeds cross-origin COEP:none",
+  parent_coep_require_corp, cross_origin, coep_none, EXPECT_BLOCK);
+iframeTest("COEP:require-corp embeds same-origin COEP:credentialless",
+  parent_coep_require_corp, same_origin, coep_credentialless, EXPECT_BLOCK);
+iframeTest("COEP:require-corp embeds cross-origin COEP:credentialless",
+  parent_coep_require_corp, cross_origin, coep_credentialless, EXPECT_BLOCK);
+iframeTest("COEP:require-corp embeds same-origin COEP:require-corp",
+  parent_coep_require_corp, same_origin, coep_require_corp, EXPECT_LOAD);
+iframeTest("COEP:require-corp embeds cross-origin COEP:require-corp",
+  parent_coep_require_corp, cross_origin, coep_require_corp, EXPECT_BLOCK);
+
+// Using CORP:cross-origin might unblock previously blocked iframes.
+iframeTestCORP("COEP:require-corp embeds same-origin COEP:none",
+  parent_coep_require_corp, same_origin, coep_none, EXPECT_BLOCK);
+iframeTestCORP("COEP:require-corp embeds cross-origin COEP:none",
+  parent_coep_require_corp, cross_origin, coep_none, EXPECT_BLOCK);
+iframeTestCORP("COEP:require-corp embeds same-origin COEP:credentialless",
+  parent_coep_require_corp, same_origin, coep_credentialless, EXPECT_BLOCK);
+iframeTestCORP("COEP:require-corp embeds cross-origin COEP:credentialless",
+  parent_coep_require_corp, cross_origin, coep_credentialless, EXPECT_BLOCK);
+iframeTestCORP("COEP:require-corp embeds same-origin COEP:require-corp",
+  parent_coep_require_corp, same_origin, coep_require_corp, EXPECT_LOAD);
+iframeTestCORP("COEP:require-corp embeds cross-origin COEP:require-corp",
+  parent_coep_require_corp, cross_origin, coep_require_corp, EXPECT_LOAD);
+
+</script>
diff --git a/html/cross-origin-embedder-policy/credentialless/resources/common.js b/html/cross-origin-embedder-policy/credentialless/resources/common.js
index be6bb0e..45fd935 100644
--- a/html/cross-origin-embedder-policy/credentialless/resources/common.js
+++ b/html/cross-origin-embedder-policy/credentialless/resources/common.js
@@ -1,9 +1,21 @@
 const directory = '/html/cross-origin-embedder-policy/credentialless';
-
 const executor_path = directory + '/resources/executor.html?pipe=';
-const coep_none = '|header(Cross-Origin-Embedder-Policy,none)';
+
+// COEP
+const coep_none =
+    '|header(Cross-Origin-Embedder-Policy,none)';
 const coep_credentialless =
     '|header(Cross-Origin-Embedder-Policy,credentialless)';
+const coep_require_corp =
+    '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+// COOP
+const coop_same_origin =
+    '|header(Cross-Origin-Opener-Policy,same-origin)';
+
+// CORP
+const corp_cross_origin =
+    '|header(Cross-Origin-Resource-Policy,cross-origin)';
 
 // Test using the modern async/await primitives are easier to read/write.
 // However they run sequentially, contrary to async_test. This is the parallel
diff --git a/html/cross-origin-embedder-policy/credentialless/resources/dispatcher.js b/html/cross-origin-embedder-policy/credentialless/resources/dispatcher.js
index d1fd1b6..ed0e1ce 100644
--- a/html/cross-origin-embedder-policy/credentialless/resources/dispatcher.js
+++ b/html/cross-origin-embedder-policy/credentialless/resources/dispatcher.js
@@ -4,10 +4,28 @@
   "/html/cross-origin-embedder-policy/credentialless/resources/dispatcher.py";
 const dispatcher_url = new URL(dispatcher_path, location.href).href;
 
+// Return a promise, limiting the number of concurrent accesses to a shared
+// resources to |max_concurrent_access|.
+const concurrencyLimiter = (max_concurrency) => {
+  let pending = 0;
+  let waiting = [];
+  return async (task) => {
+    pending++;
+    if (pending > max_concurrency)
+      await new Promise(resolve => waiting.push(resolve));
+    await task();
+    pending--;
+    waiting.shift()?.();
+  };
+}
+
+// The official web-platform-test runner sometimes drop POST requests when
+// too many are requested in parallel. Limiting this document to send only one
+// at a time fixes the issue.
+const sendLimiter = concurrencyLimiter(1);
+
 const send = async function(uuid, message) {
-  // The official web-platform-test runner sometimes drop POST requests when
-  // many are requested in parallel. Using a lock fixes the issue.
-  await navigator.locks.request("dispatcher_send", async lock => {
+  await sendLimiter(async () => {
     await fetch(dispatcher_url + `?uuid=${uuid}`, {
       method: 'POST',
       body: message
diff --git a/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js b/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js
new file mode 100644
index 0000000..1e3038e
--- /dev/null
+++ b/html/cross-origin-embedder-policy/credentialless/resources/iframeTest.js
@@ -0,0 +1,75 @@
+// One document embeds another in an iframe. Both are loaded from the network.
+// Depending on the response headers:
+// - Cross-Origin-Embedder-Policy (COEP)
+// - Cross-Origin-Resource-Policy (CORP)
+// The child must load or must be blocked.
+//
+// What to do for:
+// - COEP:credentialless
+// - COEP:credentialless-on-children
+// is currently an active open question. This test will be updated/completed
+// later.
+
+// There are no interoperable ways to check an iframe failed to load. So a
+// timeout is being used. See https://github.com/whatwg/html/issues/125
+// Moreover, we want to track progress, managing timeout explicitly allows to
+// get a per-test results, even in case of failure of one.
+setup({ explicit_timeout: true });
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+// Open a new window loaded with the given |headers|. The new document will
+// execute any script sent toward the token it returns.
+const newWindow = (headers) => {
+  const executor_token = token();
+  const url = same_origin + executor_path + headers + `&uuid=${executor_token}`;
+  const w = window.open(url);
+  add_completion_callback(() => w.close());
+  return executor_token;
+};
+
+const EXPECT_LOAD = "load";
+const EXPECT_BLOCK = "block";
+
+// Load in iframe. Control both the parent and the child headers. Check whether
+// it loads or not.
+const iframeTest = function(
+  description,
+  parent_token,
+  child_origin,
+  child_headers,
+  expectation
+) {
+  promise_test_parallel(async test => {
+    const test_token = token();
+
+    const child_token = token();
+    const child_url = child_origin + executor_path + child_headers +
+      `&uuid=${child_token}`;
+
+    send(parent_token, `
+      let iframe = document.createElement("iframe");
+      iframe.src = "${child_url}";
+      document.body.appendChild(iframe);
+    `);
+
+    send(child_token, `
+      send("${test_token}", "load");
+    `);
+
+    // There are no interoperable ways to check an iframe failed to load. So a
+    // timeout is being used.
+    // See https://github.com/whatwg/html/issues/125
+    step_timeout(()=>send(test_token, "block"), 3000);
+
+    assert_equals(await receive(test_token), expectation);
+  }, description);
+}
+
+// A decorated version of iframeTest, adding CORP:cross-origin to the child.
+const iframeTestCORP = function() {
+  arguments[0] += ", CORP:cross-origin"; // description
+  arguments[3] += corp_cross_origin;     // child_headers
+  iframeTest(...arguments);
+}