[SAA] Implementing SharedWorker support (Part 2: Expose same-site cookie IDL)

The Storage Access API provides access to unpartitioned cookies in
third-party contexts. This CL is part of a series to extend that access
to SharedWorkers.

Before we actually provide access to SharedWorkers, we need a way for
the renderer to limit the scope of SameSite cookies provided to the
worker. Normally, a worker with a first-party storage key could access
all cookies, but if access is via the SAA handle we want to limit that
access to just SameSite: None cookies.

This CL is part of a series to allow that limiting ability. For now, we
expose the option in IDL but do not implement the change needed to limit
cookie access in first-party contexts when the option is set to 'none'.
The next CL will implement this limit.

Note: The ability to request 'all' in a third-party context is not added
here nor will it be added. We verify that an exception is thrown if this
is attempted.

Explainer:
https://privacycg.github.io/saa-non-cookie-storage/shared-workers.html

Part 1: Expose same-site cookie IPC
Part 2: Expose same-site cookie IDL
Part 3: Implement same-site cookie filtering
Part 4: Add SharedWorker support to SAA handle

Bug: 1484966
Change-Id: I8c56c02025950e119e3967afd49ed684446fd6e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5251551
Reviewed-by: Weizhong Xia <weizhong@google.com>
Auto-Submit: Ari Chivukula <arichiv@chromium.org>
Commit-Queue: Ari Chivukula <arichiv@chromium.org>
Reviewed-by: Sun Yueru <yrsun@chromium.org>
Reviewed-by: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1255183}
diff --git a/workers/same-site-cookies/first-party.all.tentative.https.window.js b/workers/same-site-cookies/first-party.all.tentative.https.window.js
new file mode 100644
index 0000000..d7e5f2e
--- /dev/null
+++ b/workers/same-site-cookies/first-party.all.tentative.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (window) Set up listener for "DidStart" message and start worker.
+// Step 2 (worker) Send "DidStart" message to window.
+// Step 3 (window) Receive "DidStart" message and cleanup.
+
+async_test(t => {
+    // Step 1
+    const worker = new SharedWorker("/workers/same-site-cookies/resources/worker.js", {sameSiteCookies: "all"});
+    worker.port.onmessage = t.step_func(e => {
+        // Step 3
+        assert_equals(e.data, "DidStart", "Worker should have started");
+        t.done();
+    });
+}, "Check SharedWorker sameSiteCookies option all for first-party");
diff --git a/workers/same-site-cookies/first-party.default.tentative.https.window.js b/workers/same-site-cookies/first-party.default.tentative.https.window.js
new file mode 100644
index 0000000..b120014
--- /dev/null
+++ b/workers/same-site-cookies/first-party.default.tentative.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (window) Set up listener for "DidStart" message and start worker.
+// Step 2 (worker) Send "DidStart" message to window.
+// Step 3 (window) Receive "DidStart" message and cleanup.
+
+async_test(t => {
+    // Step 1
+    const worker = new SharedWorker("/workers/same-site-cookies/resources/worker.js");
+    worker.port.onmessage = t.step_func(e => {
+        // Step 3
+        assert_equals(e.data, "DidStart", "Worker should have started");
+        t.done();
+    });
+}, "Check SharedWorker sameSiteCookies option default for first-party");
diff --git a/workers/same-site-cookies/first-party.none.tentative.https.window.js b/workers/same-site-cookies/first-party.none.tentative.https.window.js
new file mode 100644
index 0000000..762d924
--- /dev/null
+++ b/workers/same-site-cookies/first-party.none.tentative.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (window) Set up listener for "DidStart" message and start worker.
+// Step 2 (worker) Send "DidStart" message to window.
+// Step 3 (window) Receive "DidStart" message and cleanup.
+
+async_test(t => {
+    // Step 1
+    const worker = new SharedWorker("/workers/same-site-cookies/resources/worker.js", {sameSiteCookies: "none"});
+    worker.port.onmessage = t.step_func(e => {
+        // Step 3
+        assert_equals(e.data, "DidStart", "Worker should have started");
+        t.done();
+    });
+}, "Check SharedWorker sameSiteCookies option none for first-party");
diff --git a/workers/same-site-cookies/resources/iframe-iframe.html b/workers/same-site-cookies/resources/iframe-iframe.html
new file mode 100644
index 0000000..0c55821
--- /dev/null
+++ b/workers/same-site-cookies/resources/iframe-iframe.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script>
+// Step 3 (workers/same-site-cookies/third-party.{})
+test_driver.set_test_context(window.top);
+const type = (new URLSearchParams(window.location.search)).get("type");
+let options = {};
+switch (type) {
+    case "default":
+        break;
+    case "all":
+        options.sameSiteCookies = "all";
+        break;
+    case "none":
+        options.sameSiteCookies = "none";
+        break;
+}
+// Step 5 (workers/same-site-cookies/third-party.{})
+try {
+    const worker = new SharedWorker("/workers/same-site-cookies/resources/worker.js", options);
+    worker.port.onmessage = (e) => {
+        window.top.postMessage(e.data, "*");
+    };
+} catch (_) {
+    window.top.postMessage("DidNotStart", "*");
+}
+</script>
+</body>
diff --git a/workers/same-site-cookies/resources/iframe.sub.html b/workers/same-site-cookies/resources/iframe.sub.html
new file mode 100644
index 0000000..2531f3d
--- /dev/null
+++ b/workers/same-site-cookies/resources/iframe.sub.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script>
+// Step 2 (workers/same-site-cookies/third-party.{})
+test_driver.set_test_context(window.top);
+const type = (new URLSearchParams(window.location.search)).get("type");
+let iframe = document.createElement("iframe");
+iframe.src = "https://{{hosts[][]}}:{{ports[https][0]}}/workers/same-site-cookies/resources/iframe-iframe.html?type=" + type;
+document.body.appendChild(iframe);
+</script>
+</body>
diff --git a/workers/same-site-cookies/resources/worker.js b/workers/same-site-cookies/resources/worker.js
new file mode 100644
index 0000000..658f499
--- /dev/null
+++ b/workers/same-site-cookies/resources/worker.js
@@ -0,0 +1,5 @@
+// Step 2/4 (workers/same-site-cookies/{})
+self.onconnect = (e) => {
+    e.ports[0].postMessage("DidStart");
+    self.close();
+}
diff --git a/workers/same-site-cookies/third-party.all.tentative.sub.https.window.js b/workers/same-site-cookies/third-party.all.tentative.sub.https.window.js
new file mode 100644
index 0000000..51ad1d3
--- /dev/null
+++ b/workers/same-site-cookies/third-party.all.tentative.sub.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (top-frame) Set up listener for "DidNotStart" message and open cross-site iframe.
+// Step 2 (sub-frame) Open iframe same-site to top-frame.
+// Step 3 (sub-sub-frame) Set up listener for message and start worker.
+// Step 4 (worker) Skipped.
+// Step 5 (sub-sub-frame) Worker failed to start and window messages "DidNotStart".
+// Step 6 (top-frame) Receive "DidNotStart" message and cleanup.
+
+async_test(t => {
+  // Step 1
+  window.addEventListener("message", t.step_func(e => {
+    // Step 6
+    assert_equals(e.data, "DidNotStart", "Worker should not have started");
+    t.done();
+  }));
+  let iframe = document.createElement("iframe");
+  iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/workers/same-site-cookies/resources/iframe.sub.html?type=all";
+  document.body.appendChild(iframe);
+}, "Check SharedWorker sameSiteCookies option all for third-party");
diff --git a/workers/same-site-cookies/third-party.default.tentative.sub.https.window.js b/workers/same-site-cookies/third-party.default.tentative.sub.https.window.js
new file mode 100644
index 0000000..194df38
--- /dev/null
+++ b/workers/same-site-cookies/third-party.default.tentative.sub.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (top-frame) Set up listener for "DidStart" message and open cross-site iframe.
+// Step 2 (sub-frame) Open iframe same-site to top-frame.
+// Step 3 (sub-sub-frame) Set up listener for message and start worker.
+// Step 4 (worker) Send "DidStart" message to iframe.
+// Step 5 (sub-sub-frame) Receive message and pass on to window.
+// Step 6 (top-frame) Receive "DidStart" message and cleanup.
+
+async_test(t => {
+  // Step 1
+  window.addEventListener("message", t.step_func(e => {
+    // Step 6
+    assert_equals(e.data, "DidStart", "Worker should have started");
+    t.done();
+  }));
+  let iframe = document.createElement("iframe");
+  iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/workers/same-site-cookies/resources/iframe.sub.html?type=default";
+  document.body.appendChild(iframe);
+}, "Check SharedWorker sameSiteCookies option default for third-party");
diff --git a/workers/same-site-cookies/third-party.none.tentative.sub.https.window.js b/workers/same-site-cookies/third-party.none.tentative.sub.https.window.js
new file mode 100644
index 0000000..b91b9d7
--- /dev/null
+++ b/workers/same-site-cookies/third-party.none.tentative.sub.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+// Here's the set-up for this test:
+// Step 1 (top-frame) Set up listener for "DidStart" message and open cross-site iframe.
+// Step 2 (sub-frame) Open iframe same-site to top-frame.
+// Step 3 (sub-sub-frame) Set up listener for message and start worker.
+// Step 4 (worker) Send "DidStart" message to iframe.
+// Step 5 (sub-sub-frame) Receive message and pass on to window.
+// Step 6 (top-frame) Receive "DidStart" message and cleanup.
+
+async_test(t => {
+  // Step 1
+  window.addEventListener("message", t.step_func(e => {
+    // Step 6
+    assert_equals(e.data, "DidStart", "Worker should have started");
+    t.done();
+  }));
+  let iframe = document.createElement("iframe");
+  iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/workers/same-site-cookies/resources/iframe.sub.html?type=none";
+  document.body.appendChild(iframe);
+}, "Check SharedWorker sameSiteCookies option none for third-party");