summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/anonymous-iframe
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/anonymous-iframe')
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js67
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js50
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js60
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js94
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js128
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js124
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js63
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js40
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js104
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js34
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js74
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js57
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js49
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers1
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/common.js56
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js72
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js16
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js21
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js85
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js57
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js93
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js75
-rw-r--r--testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js70
23 files changed, 1490 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js
new file mode 100644
index 0000000000..fbdeeab4ed
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/anonymous-iframe-popup.tentative.https.window.js
@@ -0,0 +1,67 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const control_iframe = document.createElement('iframe');
+const iframe_credentialless = document.createElement('iframe');
+
+promise_setup(async t => {
+ const createControlIframe = new Promise(async resolve => {
+ control_iframe.onload = resolve;
+ control_iframe.src = ORIGIN + `/common/blank.html`;
+ document.body.append(control_iframe);
+ });
+
+ const createIframeCredentialless = new Promise(async resolve => {
+ iframe_credentialless.onload = resolve;
+ iframe_credentialless.src = ORIGIN + `/common/blank.html`;
+ iframe_credentialless.credentialless = true;
+ document.body.append(iframe_credentialless);
+ });
+
+ await Promise.all([createControlIframe, createIframeCredentialless]);
+});
+
+// Create cross-origin popup from iframes. The opener should be blocked for
+// credentialless iframe and work for normal iframe.
+promise_test(async t => {
+ const control_token = token();
+ const control_src = REMOTE_ORIGIN + executor_path + `&uuid=${control_token}`;
+ const control_popup = control_iframe.contentWindow.open(control_src);
+ add_completion_callback(() => send(control_token, "close();"));
+ assert_equals(
+ control_popup.opener, control_iframe.contentWindow,
+ "Opener from normal iframe should be available.");
+
+ const credentialless_token = token();
+ const credentialless_src =
+ REMOTE_ORIGIN + executor_path + `&uuid=${credentialless_token}`;
+ const credentialless_popup =
+ iframe_credentialless.contentWindow.open(credentialless_src);
+ add_completion_callback(() => send(credentialless_token, "close();"));
+ assert_equals(credentialless_popup, null,
+ "Opener from credentialless iframe should be blocked.");
+}, 'Cross-origin popup from normal/credentiallessiframes.');
+
+// Create a same-origin popup from iframes. The opener should be blocked for
+// credentialless iframe and work for normal iframe.
+promise_test(async t => {
+ const control_token = token();
+ const control_src = ORIGIN + executor_path + `&uuid=${control_token}`;
+ const control_popup = control_iframe.contentWindow.open(control_src);
+ add_completion_callback(() => send(control_token, "close();"));
+ assert_equals(
+ control_popup.opener, control_iframe.contentWindow,
+ "Opener from normal iframe should be available.");
+
+ const credentialless_token = token();
+ const credentialless_src =
+ ORIGIN + executor_path + `&uuid=${credentialless_token}`;
+ const credentialless_popup = iframe_credentialless.contentWindow.open(credentialless_src);
+ add_completion_callback(() => send(credentialless_token, "close();"));
+ assert_equals(credentialless_popup, null,
+ "Opener from credentialless iframe should be blocked.");
+}, 'Same-origin popup from normal/credentialless iframes.');
diff --git a/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js
new file mode 100644
index 0000000000..8c9c103429
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/anonymous-window.tentative.https.window.js
@@ -0,0 +1,50 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN} = get_host_info();
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ iframe.credentialless = false;
+ document.body.appendChild(iframe);
+ iframe.credentialless = true;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_true(iframe.credentialless);
+ assert_true(iframe.contentWindow.credentialless);
+ assert_equals(undefined, iframe.contentWindow.modified);
+}, "Credentialless (false => true) => window not reused.");
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ iframe.credentialless = false;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_false(iframe.credentialless);
+ assert_false(iframe.contentWindow.credentialless);
+ assert_equals(undefined, iframe.contentWindow.modified);
+}, "Credentialless (true => false) => window not reused.");
+
+promise_test_parallel(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.credentialless = true;
+ iframe.src = ORIGIN + "/common/blank.html?pipe=status(204)";
+ document.body.appendChild(iframe);
+ iframe.credentialless = true;
+ iframe.contentWindow.modified = true;
+ iframe.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => iframe.onload = resolve);
+ assert_true(iframe.credentialless);
+ assert_true(iframe.contentWindow.credentialless);
+ assert_true(iframe.contentWindow.modified);
+}, "Credentialless (true => true) => window reused.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js
new file mode 100644
index 0000000000..b816de4cd5
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cache-storage.tentative.https.window.js
@@ -0,0 +1,60 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A script storing a value into the CacheStorage.
+const store_script = (key, value, done) => `
+ const request = new Request("/${key}.txt");
+ const response = new Response("${value}", {
+ headers: { "content-type": "plain/txt" }
+ });
+ const cache = await caches.open("v1");
+ const value = await cache.put(request, response.clone());
+ send("${done}", "stored");
+`;
+
+// A script loading a value from the CacheStorage.
+const load_script = (key, done) => `
+ const cache = await caches.open("v1");
+ const request = new Request("/${key}.txt");
+ try {
+ const response = await cache.match(request);
+ const value = await response.text();
+ send("${done}", value);
+ } catch (error) {
+ send("${done}", "not found");
+ }
+`;
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 2 actors: A credentialless iframe and a normal one.
+ const iframe_credentialless = newIframeCredentialless(origin);
+ const iframe_normal = newIframe(origin);
+ const response_queue_1 = token();
+ const response_queue_2 = token();
+
+ // 1. Each of them store a value in CacheStorage with different keys.
+ send(iframe_credentialless , store_script(key_1, "value_1", response_queue_1));
+ send(iframe_normal, store_script(key_2, "value_2", response_queue_2));
+ assert_equals(await receive(response_queue_1), "stored");
+ assert_equals(await receive(response_queue_2), "stored");
+
+ // 2. Each of them tries to retrieve the value from opposite side, without
+ // success.
+ send(iframe_credentialless , load_script(key_2, response_queue_1));
+ send(iframe_normal, load_script(key_1, response_queue_2));
+ assert_equals(await receive(response_queue_1), "not found");
+ assert_equals(await receive(response_queue_2), "not found");
+
+ // 3. Each of them tries to retrieve the value from their side, with success:
+ send(iframe_credentialless , load_script(key_1, response_queue_1));
+ send(iframe_normal, load_script(key_2, response_queue_2));
+ assert_equals(await receive(response_queue_1), "value_1");
+ assert_equals(await receive(response_queue_2), "value_2");
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js
new file mode 100644
index 0000000000..ddad924ea0
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cookie-store.tentative.https.window.js
@@ -0,0 +1,94 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A set of tests, checking cookies defined from within a credentialless iframe
+// continue to work.
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = token()
+
+const credentialless_iframe = newIframeCredentialless(cross_origin);
+
+// Install some helper functions in the child to observe Cookies:
+promise_setup(async () => {
+ await send(credentialless_iframe, `
+ window.getMyCookie = () => {
+ const value = "; " + document.cookie;
+ const parts = value.split("; ${cookie_key}=");
+ if (parts.length !== 2)
+ return undefined
+ return parts.pop().split(';').shift();
+ };
+
+ window.nextCookieValue = () => {
+ return new Promise(resolve => {
+ const old_cookie = getMyCookie();
+ let timeToLive = 40; // 40 iterations of 100ms = 4s;
+ const interval = setInterval(() => {
+ const next_cookie_value = getMyCookie();
+ timeToLive--;
+ if (old_cookie !== next_cookie_value || timeToLive <= 0) {
+ clearInterval(interval);
+ resolve(next_cookie_value)
+ }
+ }, 100)
+ });
+ };
+ `);
+}, "Setup");
+
+promise_test(async test => {
+ const this_token = token();
+ send(credentialless_iframe, `
+ document.cookie = "${cookie_key}=cookie_value_1";
+ send("${this_token}", getMyCookie());
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_1");
+}, "Set/Get cookie via JS API");
+
+promise_test(async test => {
+ const resource_token = token();
+ send(credentialless_iframe, `
+ fetch("${showRequestHeaders(cross_origin, resource_token)}");
+ `);
+
+ const request_headers = JSON.parse(await receive(resource_token));
+ const cookie_value = parseCookies(request_headers)[cookie_key];
+ assert_equals(cookie_value, "cookie_value_1");
+}, "Get Cookie via subresource requests");
+
+promise_test(async test => {
+ const resource_token = token();
+ const resource_url = cross_origin + "/common/blank.html?pipe=" +
+ `|header(Set-Cookie,${cookie_key}=cookie_value_2;Path=/common/dispatcher)`;
+ const this_token = token();
+ send(credentialless_iframe, `
+ const next_cookie_value = nextCookieValue();
+ fetch("${resource_url}");
+ send("${this_token}", await next_cookie_value);
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_2");
+}, "Set Cookie via subresource requests");
+
+promise_test(async test => {
+ const resource_token = token();
+ const resource_url = cross_origin + "/common/blank.html?pipe=" +
+ `|header(Set-Cookie,${cookie_key}=cookie_value_3;Path=/common/dispatcher)`;
+ const this_token = token();
+ send(credentialless_iframe, `
+ const next_cookie_value = nextCookieValue();
+ const iframe = document.createElement("iframe");
+ iframe.src = "${resource_url}";
+ document.body.appendChild(iframe);
+ send("${this_token}", await next_cookie_value);
+ `);
+
+ assert_equals(await receive(this_token), "cookie_value_3");
+}, "Set Cookie via navigation requests");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js
new file mode 100644
index 0000000000..d6889ae52d
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/cookie.tentative.https.window.js
@@ -0,0 +1,128 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+const same_origin = get_host_info().HTTPS_ORIGIN;
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const cookie_key = "credentialless_iframe_load_cookie";
+const cookie_same_origin = "same_origin";
+const cookie_cross_origin = "cross_origin";
+
+const cookieFromResource = async resource_token => {
+ let headers = JSON.parse(await receive(resource_token));
+ return parseCookies(headers)[cookie_key];
+};
+
+// Load a credentialless iframe, return the HTTP request cookies.
+const cookieFromCredentiallessIframeRequest = async (iframe_origin) => {
+ const resource_token = token();
+ let iframe = document.createElement("iframe");
+ iframe.src = `${showRequestHeaders(iframe_origin, resource_token)}`;
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ return await cookieFromResource(resource_token);
+};
+
+// Load a resource `type` from the iframe with `document_token`,
+// return the HTTP request cookies.
+const cookieFromResourceInIframe =
+ async (document_token, resource_origin, type = "img") => {
+ const resource_token = token();
+ send(document_token, `
+ let el = document.createElement("${type}");
+ el.src = "${showRequestHeaders(resource_origin, resource_token)}";
+ document.body.appendChild(el);
+ `);
+ return await cookieFromResource(resource_token);
+};
+
+promise_test_parallel(async test => {
+ await Promise.all([
+ setCookie(same_origin, cookie_key, cookie_same_origin),
+ setCookie(cross_origin, cookie_key, cookie_cross_origin),
+ ]);
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromCredentiallessIframeRequest(same_origin),
+ undefined
+ );
+ }, "Credentialless same-origin iframe is loaded without credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromCredentiallessIframeRequest(cross_origin),
+ undefined
+ );
+ }, "Credentialless cross-origin iframe is loaded without credentials");
+
+ const iframe_same_origin = newIframeCredentialless(same_origin);
+ const iframe_cross_origin = newIframeCredentialless(cross_origin);
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, same_origin),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send same_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, cross_origin),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send cross_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, cross_origin),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send cross_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, same_origin),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send same_origin credentials");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, same_origin,
+ "iframe"),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send same_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_same_origin, cross_origin,
+ "iframe"),
+ undefined
+ );
+ }, "same_origin credentialless iframe can't send cross_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, cross_origin,
+ "iframe"),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send cross_origin credentials "
+ + "on child iframe");
+
+ promise_test_parallel(async test => {
+ assert_equals(
+ await cookieFromResourceInIframe(iframe_cross_origin, same_origin,
+ "iframe"),
+ undefined
+ );
+ }, "cross_origin credentialless iframe can't send same_origin credentials "
+ + "on child iframe");
+
+}, "Setup")
diff --git a/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js
new file mode 100644
index 0000000000..8c5e2fa60f
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/embedding.tentative.https.window.js
@@ -0,0 +1,124 @@
+// META: variant=?1-1
+// META: variant=?2-2
+// META: variant=?3-3
+// META: variant=?4-4
+// META: variant=?5-5
+// META: variant=?6-6
+// META: variant=?7-7
+// META: variant=?8-8
+// META: variant=?9-9
+// META: variant=?10-10
+// META: variant=?11-11
+// META: variant=?12-12
+// META: variant=?13-last
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/subset-tests.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: script=./resources/embedding-test.js
+
+const {REMOTE_ORIGIN} = get_host_info();
+
+// variant = 1
+subsetTest(embeddingTest,
+ "Parent embeds same-origin credentialless iframe", {
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 2
+subsetTest(embeddingTest,
+ "Parent embeds cross-origin credentialless iframe", {
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 3
+subsetTest(embeddingTest,
+ "COEP:require-corp parent embeds same-origin credentialless iframe", {
+ parent_headers: coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 4
+subsetTest(embeddingTest,
+ "COEP:require-corp parent embeds cross-origin credentialless iframe", {
+ parent_headers: coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 5
+subsetTest(embeddingTest,
+ "COEP:credentialless parent embeds same-origin credentialless iframe", {
+ parent_headers: coep_credentialless,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 6
+subsetTest(embeddingTest,
+ "COEP:credentialless parent embeds cross-origin credentialless iframe", {
+ parent_headers: coep_credentialless,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 7
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:require-corp embeds same-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 8
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:require-corp embeds cross-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 9
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:credentialless embeds same-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_credentialless,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 10
+// Regression test for https://crbug.com/1314369
+subsetTest(embeddingTest,
+ "COOP:same-origin + COEP:credentialless embeds cross-origin credentialless iframe", {
+ parent_headers: coop_same_origin + coep_credentialless,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 11
+subsetTest(embeddingTest,
+ "Parents embeds a CSP:frame-ancestors credentialless iframe", {
+ child_headers: "|header(Content-Security-Policy,frame-ancestors 'none')",
+ expectation: EXPECT_BLOCK,
+});
+
+// variant = 12
+subsetTest(embeddingTest,
+ "Cross-Origin-Isolated parent embeds same-origin COEP credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_headers: coop_same_origin + coep_require_corp,
+ expectation: EXPECT_LOAD,
+});
+
+// variant = 13
+subsetTest(embeddingTest,
+ "Cross-Origin-Isolated parent embeds cross-origin COEP credentialless iframe", {
+ parent_headers: coop_same_origin + coep_require_corp,
+ child_headers: coop_same_origin + coep_require_corp,
+ child_origin: REMOTE_ORIGIN,
+ expectation: EXPECT_LOAD,
+});
diff --git a/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js
new file mode 100644
index 0000000000..354abffb9c
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame-bypass.tentative.https.window.js
@@ -0,0 +1,63 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: timeout=long
+
+setup(() => {
+ assert_implements(window.HTMLFencedFrameElement,
+ "HTMLFencedFrameElement is not supported.");
+})
+
+// 4 actors:
+// A (this document)
+// ┌─────────────────────┴───────┐
+// ┌─┼────────────────────────┐ D (credentialless-iframe)
+// │ B (fenced-frame) │
+// │ │ │
+// │ C (credentialless-iframe)│
+// └──────────────────────────┘
+//
+// This test whether the two credentialless iframe can communicate and bypass the
+// fencedframe boundary. This shouldn't happen.
+promise_test(async test => {
+ const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const msg_queue = token();
+
+ // Create the the 3 actors.
+ const iframe_credentialless_1 = newIframeCredentialless(cross_origin);
+ const fenced_frame = newFencedFrame(cross_origin);
+ send(fenced_frame, `
+ const importScript = ${importScript};
+ await importScript("/common/utils.js");
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ const support_loading_mode_fenced_frame =
+ "|header(Supports-Loading-Mode,fenced-frame)";
+ const iframe_credentialless_2 = newIframeCredentialless("${cross_origin}",
+ support_loading_mode_fenced_frame);
+ send("${msg_queue}", iframe_credentialless_2);
+ `);
+ const iframe_credentialless_2 = await receive(msg_queue);
+
+ // Try to communicate using BroadCastChannel, in between the credentialless
+ // iframes.
+ const bc_key = token();
+ send(iframe_credentialless_1, `
+ const bc = new BroadcastChannel("${bc_key}");
+ bc.onmessage = event => send("${msg_queue}", event.data);
+ send("${msg_queue}", "BroadcastChannel registered");
+ `);
+ assert_equals(await receive(msg_queue), "BroadcastChannel registered");
+ await send(iframe_credentialless_2, `
+ const bc = new BroadcastChannel("${bc_key}");
+ bc.postMessage("Can communicate");
+ `);
+ test.step_timeout(() => {
+ send(msg_queue, "Cannot communicate");
+ }, 4000);
+
+ assert_equals(await receive(msg_queue), "Cannot communicate");
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js
new file mode 100644
index 0000000000..675c136606
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/fenced-frame.tentative.https.window.js
@@ -0,0 +1,40 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+// META: timeout=long
+
+setup(() => {
+ assert_implements(window.HTMLFencedFrameElement,
+ "HTMLFencedFrameElement is not supported.");
+})
+
+// Check whether this credentialless bit propagates toward FencedFrame. It
+// shouldn't.
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_ORIGIN;
+ const msg_queue = token();
+
+ // 1. Create a credentialless iframe.
+ const iframe_credentialless = newIframeCredentialless(origin);
+
+ // 2. Create a FencedFrame within it.
+ send(iframe_credentialless, `
+ const importScript = ${importScript};
+ await importScript("/common/utils.js");
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ const frame_fenced = newFencedFrame("${origin}");
+ send("${msg_queue}", frame_fenced);
+ `);
+ const frame_fenced = await receive(msg_queue);
+
+ // 3. Expect it not to be considered credentialless.
+ send(frame_fenced, `
+ send("${msg_queue}", window.credentialless);
+ `);
+ assert_equals(await receive(msg_queue), "false",
+ "Check window.credentialless in FencedFrame");
+}, 'FencedFrame within a credentialless iframe is not credentialless')
diff --git a/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js
new file mode 100644
index 0000000000..d2749c6be2
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/indexeddb.tentative.https.window.js
@@ -0,0 +1,104 @@
+// META: timeout=long
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// "token()" is used to get unique value for every execution of the test. This
+// avoids potential side effects of one run toward the second.
+const g_db_store = token();
+const g_db_name = token();
+const g_db_version = 1;
+
+// A script storing "|id|=|value|" in IndexedDB.
+const write_script = (id, value, done) => `
+ // Open the database:
+ const request = indexedDB.open("${g_db_name}", "${g_db_version}");
+ request.onupgradeneeded = () => {
+ request.result.createObjectStore("${g_db_store}", {keyPath: "id"});
+ };
+ await new Promise(r => request.onsuccess = r);
+ const db = request.result;
+
+ // Write the value:
+ const transaction_write = db.transaction("${g_db_store}", "readwrite");
+ transaction_write.objectStore("${g_db_store}").add({
+ id: "${id}",
+ value: "${value}",
+ });
+ await transaction_write.complete;
+
+ db.close();
+ send("${done}", "Done");
+`;
+
+// A script retrieving what was stored inside IndexedDB.
+const read_script = (done) => `
+ // Open the database:
+ const request = indexedDB.open("${g_db_name}", "${g_db_version}");
+ await new Promise(r => request.onsuccess = r);
+ const db = request.result;
+
+ // Read:
+ const transaction_read = db.transaction("${g_db_store}", "readonly");
+ const get_all = transaction_read.objectStore("${g_db_store}").getAll();
+ await new Promise(r => transaction_read.oncomplete = r);
+
+ db.close();
+ send("${done}", JSON.stringify(get_all.result));
+`;
+
+promise_test(async test => {
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const iframes = [
+ newIframeCredentialless(origin),
+ newIframeCredentialless(origin),
+ newIframe(origin),
+ newIframe(origin),
+ ];
+
+ // 1. Write a different key-value pair from the iframes in IndexedDB:
+ const keys = iframes.map(token);
+ const values = iframes.map(token);
+ const response_queues = iframes.map(token);
+ await Promise.all(iframes.map(async (_, i) => {
+ send(iframes[i], write_script(keys[i], values[i], response_queues[i]));
+ assert_equals(await receive(response_queues[i]), "Done");
+ }));
+
+ // 2. Read the state from every iframes:
+ const states = await Promise.all(iframes.map(async (_, i) => {
+ send(iframes[i], read_script(response_queues[i]));
+ const reply = JSON.parse(await receive(response_queues[i]));
+
+ const state = {}
+ for(entry of reply)
+ state[entry.id] = entry.value;
+ return state;
+ }));
+
+
+ // Verify the two credentialless iframe share the same state and the normal
+ // iframe share a second state
+ assert_equals(states[0][keys[0]], values[0]);
+ assert_equals(states[0][keys[1]], values[1]);
+ assert_equals(states[0][keys[2]], undefined);
+ assert_equals(states[0][keys[3]], undefined);
+
+ assert_equals(states[1][keys[0]], values[0]);
+ assert_equals(states[1][keys[1]], values[1]);
+ assert_equals(states[1][keys[2]], undefined);
+ assert_equals(states[1][keys[3]], undefined);
+
+ assert_equals(states[2][keys[0]], undefined);
+ assert_equals(states[2][keys[1]], undefined);
+ assert_equals(states[2][keys[2]], values[2]);
+ assert_equals(states[2][keys[3]], values[3]);
+
+ assert_equals(states[3][keys[0]], undefined);
+ assert_equals(states[3][keys[1]], undefined);
+ assert_equals(states[3][keys[2]], values[2]);
+ assert_equals(states[3][keys[3]], values[3]);
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js
new file mode 100644
index 0000000000..8d8b095588
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/initial-empty-document.tentative.https.window.js
@@ -0,0 +1,34 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+
+const {ORIGIN} = get_host_info();
+
+promise_test_parallel(async t => {
+ const parent = document.createElement("iframe");
+ parent.credentialless = true;
+ document.body.appendChild(parent);
+ parent.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => parent.onload = resolve);
+ assert_true(parent.credentialless);
+
+ const child = document.createElement("iframe");
+ parent.contentDocument.body.appendChild(child);
+ assert_false(child.credentialless);
+ assert_true(child.contentWindow.credentialless);
+}, "Initial empty document inherits from parent's document.");
+
+promise_test_parallel(async t => {
+ const parent = document.createElement("iframe");
+ document.body.appendChild(parent);
+ parent.src = ORIGIN + "/common/blank.html";
+ // Wait for navigation to complete.
+ await new Promise(resolve => parent.onload = resolve);
+ assert_false(parent.credentialless);
+
+ const child = document.createElement("iframe");
+ child.credentialless = true;
+ parent.contentDocument.body.appendChild(child);
+ assert_true(child.credentialless);
+ assert_true(child.contentWindow.credentialless);
+}, "Initial empty document inherits from its's iframe's credentialless attribute.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js
new file mode 100644
index 0000000000..37678ff12b
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/local-storage-initial-empty-document.tentative.https.window.js
@@ -0,0 +1,74 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// This test verifies the behavior of the initial empty document nested inside
+// credentialless iframes.
+//
+// The following tree of frames and documents is used:
+// A
+// ├──B (credentialless)
+// │ └──D (initial empty document)
+// └──C (control)
+// └──E (initial empty document)
+//
+// Storage used for D and E must be different.
+promise_test(async test => {
+ const iframe_B = newIframeCredentialless(origin);
+ const iframe_C = newIframe(origin);
+
+ // Create iframe_D and store a value in localStorage.
+ const key_D = token();
+ const value_D = "value_D";
+ const queue_B = token();
+ send(iframe_B, `
+ const iframe_D = document.createElement("iframe");
+ document.body.appendChild(iframe_D);
+ iframe_D.contentWindow.localStorage.setItem("${key_D}","${value_D}");
+ send("${queue_B}", "Done");
+ `);
+
+ // Create iframe_E and store a value in localStorage.
+ const key_E = token();
+ const value_E = "value_E";
+ const queue_C = token();
+ send(iframe_C, `
+ const iframe_E = document.createElement("iframe");
+ document.body.appendChild(iframe_E);
+ iframe_E.contentWindow.localStorage.setItem("${key_E}","${value_E}");
+ send("${queue_C}", "Done");
+ `);
+
+ assert_equals(await receive(queue_B), "Done");
+ assert_equals(await receive(queue_C), "Done");
+
+ // Try to load both values from both contexts:
+ send(iframe_B, `
+ const iframe_D = document.querySelector("iframe");
+ const value_D = iframe_D.contentWindow.localStorage.getItem("${key_D}");
+ const value_E = iframe_D.contentWindow.localStorage.getItem("${key_E}");
+ send("${queue_B}", value_D);
+ send("${queue_B}", value_E);
+ `);
+ send(iframe_C, `
+ const iframe_E = document.querySelector("iframe");
+ const value_D = iframe_E.contentWindow.localStorage.getItem("${key_D}");
+ const value_E = iframe_E.contentWindow.localStorage.getItem("${key_E}");
+ send("${queue_C}", value_D);
+ send("${queue_C}", value_E);
+ `);
+
+ // Verify the credentialless iframe and the normal one do not have access to
+ // each other.
+ assert_equals(await receive(queue_B), value_D, // key_D
+ "Credentialless iframe can access credentialless context");
+ assert_equals(await receive(queue_B), "", // key_E
+ "Credentialless iframe can't access credentialled context");
+ assert_equals(await receive(queue_C), "", // key_D
+ "Credentialled iframe can't access credentialless context");
+ assert_equals(await receive(queue_C), value_E, // key_E
+ "Credentialled iframe can access credentialled context");
+}, "Local storage is correctly partitioned with regards to credentialless " +
+ "iframe in initial empty documents.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js
new file mode 100644
index 0000000000..bd80ff4729
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/local-storage.tentative.https.window.js
@@ -0,0 +1,57 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Make |iframe| to store |key|=|value| into LocalStorage.
+const store = async (iframe, key, value) => {
+ const response_queue = token();
+ send(iframe, `
+ localStorage.setItem("${key}", "${value}");
+ send("${response_queue}", "stored");
+ `);
+ assert_equals(await receive(response_queue), "stored");
+};
+
+// Make |iframe| to load |key| in LocalStorage. Check it matches the
+// |expected_value|.
+const load = async (iframe, key, expected_value) => {
+ const response_queue = token();
+ send(iframe, `
+ const value = localStorage.getItem("${key}");
+ send("${response_queue}", value || "not found");
+ `);
+ assert_equals(await receive(response_queue), expected_value);
+};
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const iframe_credentialless_1 = newIframeCredentialless(origin);
+ const iframe_credentialless_2 = newIframeCredentialless(origin);
+ const iframe_normal_1 = newIframe(origin);
+ const iframe_normal_2 = newIframe(origin);
+
+ // 1. Store a value in one credentialless iframe and one normal iframe.
+ await Promise.all([
+ store(iframe_credentialless_1, key_1, "value_1"),
+ store(iframe_normal_1, key_2, "value_2"),
+ ]);
+
+ // 2. Check what each of them can retrieve.
+ await Promise.all([
+ load(iframe_credentialless_1, key_1, "value_1"),
+ load(iframe_credentialless_2, key_1, "value_1"),
+ load(iframe_credentialless_1, key_2, "not found"),
+ load(iframe_credentialless_2, key_2, "not found"),
+
+ load(iframe_normal_1, key_1, "not found"),
+ load(iframe_normal_2, key_1, "not found"),
+ load(iframe_normal_1, key_2, "value_2"),
+ load(iframe_normal_2, key_2, "value_2"),
+ ]);
+}, "Local storage is correctly partitioned with regards to credentialless iframe");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js
new file mode 100644
index 0000000000..ccaa41f9ef
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js
@@ -0,0 +1,49 @@
+// META: script=/common/utils.js
+
+promise_test(async t => {
+ let iframe_allowed = (iframe) => new Promise(async resolve => {
+ window.addEventListener("message", t.step_func(msg => {
+ if (msg.source !== iframe.contentWindow) return;
+ assert_equals(msg.data, "loaded",
+ "Unexpected message from broadcast channel.");
+ resolve(true);
+ }));
+
+ // To see whether the iframe was blocked, we check whether it
+ // becomes cross-origin (since error pages are loaded cross-origin).
+ await t.step_wait(() => {
+ try {
+ // Accessing contentWindow.location.href cross-origin throws.
+ iframe.contentWindow.location.href === null;
+ return false;
+ } catch {
+ return true;
+ }
+ });
+ resolve(false);
+ });
+
+ // Create a credentialless child iframe.
+ const child = document.createElement("iframe");
+ child.credentialless = true;
+ t.add_cleanup(() => child.remove());
+
+ child.src = "/html/cross-origin-embedder-policy/resources/" +
+ "navigate-none.sub.html?postMessageTo=top";
+ document.body.append(child);
+
+ assert_true(await iframe_allowed(child),
+ "The credentialless iframe should be allowed.");
+
+ // Create a child of the credentialless iframe. Even if the grandchild
+ // does not have the 'credentialless' attribute set, it inherits the
+ // credentialless property from the parent.
+ const grandchild = child.contentDocument.createElement("iframe");
+
+ grandchild.src = "/html/cross-origin-embedder-policy/resources/" +
+ "navigate-none.sub.html?postMessageTo=top";
+ child.contentDocument.body.append(grandchild);
+
+ assert_true(await iframe_allowed(grandchild),
+ "The child of the credentialless iframe should be allowed.");
+}, 'Loading a credentialless iframe with COEP: require-corp is allowed.');
diff --git a/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/require-corp-embed-anonymous-iframe.tentative.https.window.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/common.js b/testing/web-platform/tests/html/anonymous-iframe/resources/common.js
new file mode 100644
index 0000000000..241df1df24
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/common.js
@@ -0,0 +1,56 @@
+// Create a credentialless iframe. The new document will execute any scripts
+// sent toward the token it returns.
+const newIframeCredentialless = (child_origin, opt_headers) => {
+ opt_headers ||= "";
+ const sub_document_token = token();
+ let iframe = document.createElement('iframe');
+ iframe.src = child_origin + executor_path + opt_headers +
+ `&uuid=${sub_document_token}`;
+ iframe.credentialless = true;
+ document.body.appendChild(iframe);
+ return sub_document_token;
+};
+
+// Create a normal iframe. The new document will execute any scripts sent
+// toward the token it returns.
+const newIframe = (child_origin) => {
+ const sub_document_token = token();
+ let iframe = document.createElement('iframe');
+ iframe.src = child_origin + executor_path + `&uuid=${sub_document_token}`;
+ iframe.credentialless = false
+ document.body.appendChild(iframe);
+ return sub_document_token;
+};
+
+// Create a popup. The new document will execute any scripts sent toward the
+// token it returns.
+const newPopup = (test, origin) => {
+ const popup_token = token();
+ const popup = window.open(origin + executor_path + `&uuid=${popup_token}`);
+ test.add_cleanup(() => popup.close());
+ return popup_token;
+}
+
+// Create a fenced frame. The new document will execute any scripts sent
+// toward the token it returns.
+const newFencedFrame = (child_origin) => {
+ const support_loading_mode_fenced_frame =
+ "|header(Supports-Loading-Mode,fenced-frame)";
+ const sub_document_token = token();
+ const fencedframe = document.createElement('fencedframe');
+ const url = child_origin + executor_path +
+ support_loading_mode_fenced_frame +
+ `&uuid=${sub_document_token}`;
+ fencedframe.config = new FencedFrameConfig(url);
+ document.body.appendChild(fencedframe);
+ return sub_document_token;
+};
+
+const importScript = (url) => {
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = url;
+ const loaded = new Promise(resolve => script.onload = resolve);
+ document.body.appendChild(script);
+ return loaded;
+}
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js b/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js
new file mode 100644
index 0000000000..fb5d11efa0
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/embedding-test.js
@@ -0,0 +1,72 @@
+// One document embeds another in an iframe. Both are loaded from the network.
+// Check whether or not the child can 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
+// 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 EXPECT_LOAD = "load";
+const EXPECT_BLOCK = "block";
+
+// Load a credentialless iframe. Control both the parent and the child headers.
+// Check whether it loaded or not.
+const embeddingTest = (description, {
+ parent_headers,
+ child_headers,
+ child_origin,
+ expectation,
+}) => {
+ // Default values:
+ child_origin ||= globalThis.origin;
+ parent_headers ||= "";
+ child_headers||= "";
+
+ const parent_origin = window.origin;
+
+ promise_test_parallel(async test => {
+ const parent_token = token();
+ const parent_url = parent_origin + executor_path + parent_headers +
+ `&uuid=${parent_token}`;
+
+ const child_token = token();
+ const child_url = child_origin + executor_path + child_headers +
+ `&uuid=${child_token}`;
+
+ // Create the parent:
+ window.open(parent_url);
+ add_completion_callback(() => send(parent_token, "close()"));
+
+ // The parent creates its child:
+ await send(parent_token, `
+ const iframe = document.createElement("iframe");
+ iframe.credentialless = true;
+ iframe.src = "${child_url}";
+ document.body.appendChild(iframe);
+ `);
+
+ // Ping the child to know whether it was allowed to load or not:
+ const reply_token = token();
+ await send(child_token, `
+ send("${reply_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
+ // Use a shorter timeout when it is expected to be reached.
+ // - The long delay reduces the false-positive rate. False-positive causes
+ // stability problems on bot, so a big delay is used to vanish them.
+ // https://crbug.com/1215956.
+ // - The short delay avoids delaying too much the test(s) for nothing and
+ // timing out. False-negative are not a problem, they just need not to
+ // overwhelm the true-negative, which is trivial to get.
+ step_timeout(() => send(reply_token, "block"), expectation == EXPECT_BLOCK
+ ? 1500
+ : 3500
+ );
+
+ assert_equals(await receive(reply_token), expectation);
+ }, description);
+};
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js b/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js
new file mode 100644
index 0000000000..288ad5954e
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/serviceworker-partitioning-helper.js
@@ -0,0 +1,16 @@
+let messages = {};
+let ports = {};
+
+self.addEventListener("message", e => {
+ const from = e.data.from;
+ const check = e.data.check;
+
+ if (from) {
+ messages[from] = true;
+ ports[from] = e.ports[0];
+ }
+
+ if (check) {
+ ports[check].postMessage(messages);
+ }
+});
diff --git a/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js b/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js
new file mode 100644
index 0000000000..8b416c33d7
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/resources/sharedworker-partitioning-helper.js
@@ -0,0 +1,21 @@
+let messages = {};
+
+onconnect = function(e) {
+ let port = e.ports[0];
+
+ port.addEventListener('message', function(e) {
+ const action = e.data.action;
+ const from = e.data.from;
+
+ if (action === 'record') {
+ messages[from] = true;
+ port.postMessage({ack: from});
+ }
+
+ if (action === 'retrieve') {
+ port.postMessage({ack: from, messages: messages});
+ }
+ });
+
+ port.start();
+};
diff --git a/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js
new file mode 100644
index 0000000000..14997a4754
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/serviceworker-partitioning.tentative.https.window.js
@@ -0,0 +1,85 @@
+// META: script=/common/utils.js
+
+const sw_url = location.pathname.replace(/[^/]*$/, '') +
+ "./resources/serviceworker-partitioning-helper.js";
+
+promise_test(async t => {
+ // Create 4 iframes (two normal and two credentialless ones) and register
+ // a serviceworker with the same scope and url in all of them.
+ //
+ // Registering the same service worker again with the same url and
+ // scope is a no-op. However, credentialless iframes get partitioned
+ // service workers, so we should have a total of 2 service workers
+ // at the end (one for the normal iframes and one for the credentialless
+ // ones).
+ let iframes = await Promise.all([
+ { name: "normal", credentialless: false},
+ { name: "normal_control", credentialless: false},
+ { name: "credentialless", credentialless: true},
+ { name: "credentialless_control", credentialless: true},
+ ].map(async ({name, credentialless}) => {
+
+ let iframe = await new Promise(resolve => {
+ let iframe = document.createElement('iframe');
+ iframe.onload = () => resolve(iframe);
+ iframe.src = '/common/blank.html';
+ if (credentialless) iframe.credentialless = true;
+ document.body.append(iframe);
+ });
+
+ let sw = await new Promise(resolve => {
+ iframe.contentWindow.navigator.serviceWorker.register(sw_url)
+ .then(r => {
+ add_completion_callback(_ => r.unregister());
+ resolve(r.active || r.installing || r.waiting);
+ });
+ });
+ return { iframe: iframe, name: name, sw: sw };
+ }));
+
+ // Setup a MessageChannel for each pair (iframe, serviceworker).
+ // Ping each serviceworker telling him which iframe it belongs to.
+ iframes.forEach((iframe, i) => {
+ iframe.channel = new MessageChannel();
+ iframe.sw.postMessage({ from: iframe.name }, [iframe.channel.port2]);
+ });
+
+ let msg_promises = iframes.map(iframe => new Promise(resolve => {
+ iframe.channel.port1.onmessage = event => resolve(event.data);
+ }));
+
+ // Ping each (iframe, serviceworker) asking for which messages it got.
+ iframes.map(iframe => iframe.sw.postMessage({ check: iframe.name }));
+
+ // Collect all replies.
+ let msgs = await Promise.all(msg_promises);
+
+ // The "normal" iframe serviceworker belongs to the "normal" and the
+ // "normal_control" iframes.
+ assert_true(!!msgs[0]["normal"]);
+ assert_true(!!msgs[0]["normal_control"]);
+ assert_false(!!msgs[0]["credentialless"]);
+ assert_false(!!msgs[0]["credentialless_control"]);
+
+ // The "normal_control" iframe shares the same serviceworker as the "normal"
+ // iframe.
+ assert_true(!!msgs[1]["normal"]);
+ assert_true(!!msgs[1]["normal_control"]);
+ assert_false(!!msgs[1]["credentialless"]);
+ assert_false(!!msgs[1]["credentialless_control"]);
+
+ // The "credentialless" iframe serviceworker belongs to the "credentialless"
+ // and the "credentialless_control" iframes.
+ assert_false(!!msgs[2]["normal"]);
+ assert_false(!!msgs[2]["normal_control"]);
+ assert_true(!!msgs[2]["credentialless"]);
+ assert_true(!!msgs[2]["credentialless_control"]);
+
+ // The "credentialless_control" iframe shares the same serviceworker as the
+ // "credentialless" iframe.
+ assert_false(!!msgs[3]["normal"]);
+ assert_false(!!msgs[3]["normal_control"]);
+ assert_true(!!msgs[3]["credentialless"]);
+ assert_true(!!msgs[3]["credentialless_control"]);
+
+}, "credentialless iframes get partitioned service workers.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js
new file mode 100644
index 0000000000..425886ce38
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/session-storage.tentative.https.window.js
@@ -0,0 +1,57 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Make |iframe| to store |key|=|value| into sessionStorage.
+const store = async (iframe, key, value) => {
+ const response_queue = token();
+ send(iframe, `
+ sessionStorage.setItem("${key}", "${value}");
+ send("${response_queue}", "stored");
+ `);
+ assert_equals(await receive(response_queue), "stored");
+};
+
+// Make |iframe| to load |key| in sessionStorage. Check it matches the
+// |expected_value|.
+const load = async (iframe, key, expected_value) => {
+ const response_queue = token();
+ send(iframe, `
+ const value = sessionStorage.getItem("${key}");
+ send("${response_queue}", value || "not found");
+ `);
+ assert_equals(await receive(response_queue), expected_value);
+};
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 4 actors: 2 credentialless iframe and 2 normal iframe.
+ const iframe_credentialless_1 = newIframeCredentialless(origin);
+ const iframe_credentialless_2 = newIframeCredentialless(origin);
+ const iframe_normal_1 = newIframe(origin);
+ const iframe_normal_2 = newIframe(origin);
+
+ // 1. Store a value in one credentialless iframe and one normal iframe.
+ await Promise.all([
+ store(iframe_credentialless_1, key_1, "value_1"),
+ store(iframe_normal_1, key_2, "value_2"),
+ ]);
+
+ // 2. Check what each of them can retrieve.
+ await Promise.all([
+ load(iframe_credentialless_1, key_1, "value_1"),
+ load(iframe_credentialless_2, key_1, "value_1"),
+ load(iframe_credentialless_1, key_2, "not found"),
+ load(iframe_credentialless_2, key_2, "not found"),
+
+ load(iframe_normal_1, key_1, "not found"),
+ load(iframe_normal_2, key_1, "not found"),
+ load(iframe_normal_1, key_2, "value_2"),
+ load(iframe_normal_2, key_2, "value_2"),
+ ]);
+}, "Session storage is correctly partitioned with regards to credentialless iframe");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js
new file mode 100644
index 0000000000..6c373ee490
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/sharedworker-partitioning.tentative.https.window.js
@@ -0,0 +1,93 @@
+// META: script=/common/utils.js
+
+const sw_url = location.pathname.replace(/[^/]*$/, '') +
+ "./resources/sharedworker-partitioning-helper.js";
+
+promise_test(async t => {
+ // Create 4 iframes (two normal and two credentialless ones) and create
+ // a shared worker with the same url in all of them.
+ //
+ // Creating the same shared worker again with the same url is a
+ // no-op. However, credentialless iframes get partitioned shared workers,
+ // so we should have a total of 2 shared workers at the end (one for
+ // the normal iframes and one for the credentialless ones).
+ let iframes = await Promise.all([
+ { name: "normal", credentialless: false},
+ { name: "normal_control", credentialless: false},
+ { name: "credentialless", credentialless: true},
+ { name: "credentialless_control", credentialless: true},
+ ].map(async ({name, credentialless}) => {
+
+ let iframe = await new Promise(resolve => {
+ let iframe = document.createElement('iframe');
+ iframe.onload = () => resolve(iframe);
+ iframe.src = '/common/blank.html';
+ if (credentialless) iframe.credentialless = true;
+ document.body.append(iframe);
+ });
+
+ let sw = new iframe.contentWindow.SharedWorker(sw_url);
+ return { iframe: iframe, name: name, sw: sw };
+ }));
+
+ // Ping each worker telling him which iframe it belongs to.
+ await Promise.all(iframes.map(iframe => {
+ iframe.sw.port.postMessage({ action: 'record', from: iframe.name});
+ return new Promise(resolve => {
+ iframe.sw.port.onmessage = event => {
+ if (event.data.ack === iframe.name) resolve();
+ }
+ });
+ }));
+
+ // Ping each (iframe, sharedworker) asking for which messages it got.
+ let msgs = await Promise.all(iframes.map(iframe => {
+ iframe.sw.port.postMessage({ action: 'retrieve', from: iframe.name });
+ return new Promise(resolve => {
+ iframe.sw.port.onmessage = event => {
+ if (event.data.ack === iframe.name) resolve(event.data.messages);
+ }
+ });
+ }));
+
+ // The "normal" iframe sharedworker belongs to the "normal" and the
+ // "normal_control" iframes.
+ assert_true(!!msgs[0]["normal"] &&
+ !!msgs[0]["normal_control"] &&
+ !msgs[0]["credentialless"] &&
+ !msgs[0]["credentialless_control"],
+ 'The "normal" iframe\'s sharedworker should return ' +
+ '{"normal": true, "normal_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[0]));
+
+ // The "normal_control" iframe shares the same sharedworker as the "normal"
+ // iframe.
+ assert_true(!!msgs[1]["normal"] &&
+ !!msgs[1]["normal_control"] &&
+ !msgs[1]["credentialless"] &&
+ !msgs[1]["credentialless_control"],
+ 'The "normal_control" iframe\'s sharedworker should return ' +
+ '{"normal": true, "normal_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[1]));
+
+ // The "credentialless" iframe sharedworker belongs to the "credentialless" and the
+ // "credentialless_control" iframes.
+ assert_true(!msgs[2]["normal"] &&
+ !msgs[2]["normal_control"] &&
+ !!msgs[2]["credentialless"] &&
+ !!msgs[2]["credentialless_control"],
+ 'The "credentialless" iframe\'s sharedworker should return ' +
+ '{"credentialless": true, "credentialless_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[2]));
+
+ // The "credentialless_control" iframe shares the same sharedworker as
+ // the "credentialless" iframe.
+ assert_true(!msgs[3]["normal"] &&
+ !msgs[3]["normal_control"] &&
+ !!msgs[3]["credentialless"] &&
+ !!msgs[3]["credentialless_control"],
+ 'The "credentialless_control" iframe\'s sharedworker should return ' +
+ '{"credentialless": true, "credentialless_control": true}, ' +
+ 'but instead returned ' + JSON.stringify(msgs[3]));
+
+}, "credentialless iframes get partitioned shared workers.");
diff --git a/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js
new file mode 100644
index 0000000000..fbee4ad28e
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/web-lock.tentative.https.window.js
@@ -0,0 +1,75 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A script acquiring a lock. It can be released using window.releaseLocks
+const acquire_script = (key, response) => `
+ window.releaseLocks ||= [];
+ navigator.locks.request("${key}", async lock => {
+ send("${response}", "locked")
+ await new Promise(r => releaseLocks.push(r));
+ send("${response}", "unlocked");
+ });
+`;
+
+const release_script = (response) => `
+ for (release of releaseLocks)
+ release();
+`;
+
+// Assert that |context| holds |expected_keys|.
+const assertHeldKeys = async (context, expected_keys) => {
+ const queue = token();
+ send(context, `
+ const list = await navigator.locks.query();
+ send("${queue}", JSON.stringify(list));
+ `);
+ const state = JSON.parse(await receive(queue));
+ const held = state.held.map(x => x.name);
+ assert_equals(held.length, expected_keys.length);
+ assert_array_equals(held.sort(), expected_keys.sort());
+}
+
+promise_test(async test => {
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const key_1 = token();
+ const key_2 = token();
+
+ // 2 actors: A credentialless iframe and a normal one.
+ const iframe_credentialless = newIframeCredentialless(origin);
+ const iframe_normal = newIframe(origin);
+ const response_queue_1 = token();
+ const response_queue_2 = token();
+
+ // 1. Hold two different locks on both sides.
+ send(iframe_credentialless, acquire_script(key_1, response_queue_1));
+ send(iframe_normal, acquire_script(key_2, response_queue_2));
+ assert_equals(await receive(response_queue_1), "locked");
+ assert_equals(await receive(response_queue_2), "locked");
+ await assertHeldKeys(iframe_credentialless, [key_1]);
+ await assertHeldKeys(iframe_normal, [key_2]);
+
+ // 2. Try to acquire the lock with the same key on the opposite side. It
+ // shouldn't block, because they are partitioned.
+ send(iframe_credentialless , acquire_script(key_2, response_queue_1));
+ send(iframe_normal, acquire_script(key_1, response_queue_2));
+ assert_equals(await receive(response_queue_1), "locked");
+ assert_equals(await receive(response_queue_2), "locked");
+ await assertHeldKeys(iframe_credentialless, [key_1, key_2]);
+ await assertHeldKeys(iframe_normal, [key_1, key_2]);
+
+ // 3. Cleanup: release the 4 locks (2 on each sides).
+ send(iframe_credentialless, release_script(response_queue_1));
+ assert_equals(await receive(response_queue_1), "unlocked");
+ assert_equals(await receive(response_queue_1), "unlocked");
+ await assertHeldKeys(iframe_credentialless, []);
+ await assertHeldKeys(iframe_normal, [key_1, key_2]);
+
+ send(iframe_normal, release_script(response_queue_2));
+ assert_equals(await receive(response_queue_2), "unlocked");
+ assert_equals(await receive(response_queue_2), "unlocked");
+ await assertHeldKeys(iframe_credentialless, []);
+ await assertHeldKeys(iframe_normal, []);
+})
diff --git a/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js b/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js
new file mode 100644
index 0000000000..8c25306baf
--- /dev/null
+++ b/testing/web-platform/tests/html/anonymous-iframe/worker-cookies.tentative.https.window.js
@@ -0,0 +1,70 @@
+// META: timeout=long
+// META: variant=?worker=dedicated_worker
+// META: variant=?worker=shared_worker
+// META: variant=?worker=service_worker
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/html/cross-origin-embedder-policy/credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// Execute the same set of tests for every type of worker.
+// - DedicatedWorkers
+// - SharedWorkers
+// - ServiceWorkers.
+const params = new URLSearchParams(document.location.search);
+const worker_param = params.get("worker") || "dedicated_worker";
+
+const cookie_key = token();
+const cookie_value = "cookie_value";
+const cookie_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+
+// Create worker spawned from `context` and return its uuid.
+const workerFrom = context => {
+ const reply = token();
+ send(context, `
+ for(deps of [
+ "/common/utils.js",
+ "/resources/testharness.js",
+ "/html/cross-origin-embedder-policy/credentialless/resources/common.js",
+ ]) {
+ await new Promise(resolve => {
+ const script = document.createElement("script");
+ script.src = deps;
+ script.onload = resolve;
+ document.body.appendChild(script);
+ });
+ }
+
+ const worker_constructor = environments["${worker_param}"];
+ const headers = "";
+ const [worker, error] = worker_constructor(headers);
+ send("${reply}", worker);
+ `);
+ return receive(reply);
+};
+
+// Set a cookie from a top-level document.
+promise_test(async test => {
+ await setCookie(cookie_origin, cookie_key, cookie_value);
+}, "set cookies");
+
+// Control: iframe is not credentialless. The worker can access cookies.
+promise_test(async test => {
+ const headers = token();
+ send(await workerFrom(newIframe(cookie_origin)), `
+ fetch("${showRequestHeaders(cookie_origin, headers)}");
+ `);
+ const cookie = parseCookies(JSON.parse(await receive(headers)));
+ assert_equals(cookie[cookie_key], cookie_value)
+}, "Worker spawned from normal iframe can access global cookies");
+
+// Experiment: iframe is credentialless.
+promise_test(async test => {
+ const headers = token();
+ send(await workerFrom(newIframeCredentialless(cookie_origin)), `
+ fetch("${showRequestHeaders(cookie_origin, headers)}");
+ `);
+ const cookie = parseCookies(JSON.parse(await receive(headers)));
+ assert_equals(cookie[cookie_key], undefined)
+}, "Worker spawned from credentialless iframe can't access global cookies");