diff --git a/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js b/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js
new file mode 100644
index 00000000000000..dd369736419b61
--- /dev/null
+++ b/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/partitioned-popins/resources/proxy-helpers.js
+
+'use strict';
+
+// Spec: https://explainers-by-googlers.github.io/partitioned-popins/
+// Step 1 (window) Set up listener to resolve messages as they come in.
+// Step 2 (window) Open cross-site popin.
+// Step 3 (popin) Set up listener to resolve messages as they come in.
+// Step 4 (popin) Test and report usable methods against window.
+// Step 5 (window) Test and compare usable methods against popin.
+// Step 6 (popin) Cleanup.
+// Step 7 (window) Cleanup.
+
+async_test(t => {
+ let popin_proxy;
+
+ // Step 1
+ window.addEventListener("message", t.step_func(e => {
+ switch (e.data.type) {
+ case 'ready':
+ // Step 5
+ assert_equals(e.data.message, "Closed,Then,");
+ assert_equals(getUsableMethods(popin_proxy), "Closed,Then,");
+ popin_proxy.postMessage({type: "cleanup"}, "*");
+ break;
+ case 'cleanup':
+ // Step 7
+ t.done();
+ break;
+ }
+ }));
+
+ // Step 2
+ popin_proxy = window.open("https://{{hosts[alt][]}}:{{ports[https][0]}}/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin');
+}, "Verify cross-site Partitioned Popins proxies only have access to postMessage and closed methods.");
diff --git a/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js b/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js
new file mode 100644
index 00000000000000..1bda23eb7b6c6a
--- /dev/null
+++ b/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js
@@ -0,0 +1,39 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/partitioned-popins/resources/proxy-helpers.js
+
+'use strict';
+
+// Spec: https://explainers-by-googlers.github.io/partitioned-popins/
+// Step 1 (window) Set up listener to resolve messages as they come in.
+// Step 2 (window) Open same-origin popin.
+// Step 3 (popin) Set up listener to resolve messages as they come in.
+// Step 4 (popin) Test and report usable methods against window.
+// Step 5 (window) Test and compare usable methods against popin.
+// Step 6 (popin) Cleanup.
+// Step 7 (window) Cleanup.
+
+// TODO(crbug.com/340606651): Remove expectations file and secure same-origin popins.
+
+async_test(t => {
+ let popin_proxy;
+
+ // Step 1
+ window.addEventListener("message", t.step_func(e => {
+ switch (e.data.type) {
+ case 'ready':
+ // Step 5
+ assert_equals(e.data.message, "Closed,Then,");
+ assert_equals(getUsableMethods(popin_proxy), "Closed,Then,");
+ popin_proxy.postMessage({type: "cleanup"}, "*");
+ break;
+ case 'cleanup':
+ // Step 7
+ t.done();
+ break;
+ }
+ }));
+
+ // Step 2
+ popin_proxy = window.open("/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin');
+}, "Verify same-origin Partitioned Popins proxies only have access to postMessage and closed methods.");
diff --git a/partitioned-popins/resources/partitioned-popins.proxy-popin.html b/partitioned-popins/resources/partitioned-popins.proxy-popin.html
new file mode 100644
index 00000000000000..d1b7b869338af2
--- /dev/null
+++ b/partitioned-popins/resources/partitioned-popins.proxy-popin.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/partitioned-popins/resources/proxy-helpers.js b/partitioned-popins/resources/proxy-helpers.js
new file mode 100644
index 00000000000000..46359df0dc7601
--- /dev/null
+++ b/partitioned-popins/resources/proxy-helpers.js
@@ -0,0 +1,58 @@
+'use strict';
+
+function customMethod() {
+}
+
+let customAttribute = "";
+
+function getUsableMethods(proxy) {
+ let message = "";
+ try {
+ proxy.closed;
+ message += "Closed,"
+ } catch (_) {}
+ try {
+ proxy.blur();
+ message += "Blur,"
+ } catch (_) {}
+ try {
+ proxy.onblur;
+ message += "OnBlur,"
+ } catch (_) {}
+ try {
+ proxy.opener;
+ message += "Opener,"
+ } catch (_) {}
+ try {
+ proxy.length;
+ message += "Length,"
+ } catch (_) {}
+ try {
+ proxy.name = "foo";
+ message += "Name,"
+ } catch (_) {}
+ try {
+ proxy[0];
+ message += "AnonymousIndex,"
+ } catch (_) {}
+ try {
+ proxy['test'];
+ message += "AnonymousName,"
+ } catch (_) {}
+ try {
+ proxy.customMethod();
+ message += "CustomMethod,"
+ } catch (_) {}
+ try {
+ proxy.customAttribute;
+ message += "CustomAttributeGet,"
+ } catch (_) {}
+ try {
+ proxy.customAttribute = "";
+ message += "CustomAttributeSet,"
+ } catch (_) {}
+ if (proxy.then == undefined) {
+ message += "Then,"
+ }
+ return message;
+}