[FedCM] Allow revoke to be invoked with identity-credentials-get

Bug: 1473134
Change-Id: Icafb06c19136f18add3060f1e787595b8cddec52
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5046970
Reviewed-by: Yi Gu <yigu@chromium.org>
Commit-Queue: Nicolás Peña <npm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1227038}
diff --git a/credential-management/fedcm-revoke-iframe.sub.https.html b/credential-management/fedcm-revoke-iframe.sub.https.html
new file mode 100644
index 0000000..6365bc7
--- /dev/null
+++ b/credential-management/fedcm-revoke-iframe.sub.https.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>Federated Credential Management API revoke() tests in iframes.</title>
+<link rel="help" href="https://fedidcg.github.io/FedCM">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+
+<script type="module">
+import {alt_manifest_origin,
+        alt_request_options_with_mediation_required,
+        fedcm_test,
+        fedcm_get_and_select_first_account} from './support/fedcm-helper.sub.js';
+
+async function createIframeAndWaitForMessage(test, iframeUrl, allow = false) {
+  const messageWatcher = new EventWatcher(test, window, "message");
+  let iframe = document.createElement("iframe");
+  iframe.src = iframeUrl;
+  if (allow) {
+    iframe.allow = "identity-credentials-get";
+  }
+  document.body.appendChild(iframe);
+  const message = await messageWatcher.wait_for("message");
+  return message.data;
+}
+
+fedcm_test(async t => {
+  const message = await createIframeAndWaitForMessage(t, 'support/fedcm/revoke-iframe.html');
+  assert_equals(message.result, "Pass");
+}, 'Same-origin iframe does not need explicit identity-credentials-get');
+
+fedcm_test(async t => {
+  const message = await createIframeAndWaitForMessage(t,
+      'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/revoke-iframe.html?skip_get');
+  assert_equals(message.result, "Failed revoke");
+  assert_equals(message.errorType, "NotAllowedError");
+}, 'Cross-origin iframe fails revoke() without explicit identity-credentials-get');
+
+fedcm_test(async t => {
+  const message = await createIframeAndWaitForMessage(t,
+      'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/revoke-iframe.html',
+      /*allow=*/true);
+  assert_equals(message.result, "Pass");
+}, 'Cross-origin iframe can revoke with explicit identity-credentials-get');
+</script>
diff --git a/credential-management/support/fedcm/revoke-iframe.html b/credential-management/support/fedcm/revoke-iframe.html
new file mode 100644
index 0000000..e9a8dd1
--- /dev/null
+++ b/credential-management/support/fedcm/revoke-iframe.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script type="module">
+  import {revoke_options,
+          request_options_with_mediation_required,
+        set_fedcm_cookie, manifest_origin} from './../fedcm-helper.sub.js';
+
+// Loading this iframe in the test will make a FedCM call on load, and
+// trigger a postMessage upon completion.
+//
+// message {
+//   string result: "Pass" | "Failed get" | "Failed revoke"
+//   string errorType: error.name
+// }
+async function attemptRevoke() {
+  try {
+    await IdentityCredential.revoke(revoke_options("1234"));
+    window.top.postMessage({result: "Pass"}, "*");
+  } catch (error) {
+    window.top.postMessage({result: "Failed revoke", errorType: error.name}, "*");
+  }
+}
+
+window.onload = async () => {
+  const params = new URLSearchParams(document.location.search);
+  if (params.has("skip_get")) {
+    attemptRevoke();
+    return;
+  }
+
+  // Use this variable to stop trying to select an account once the get() promise is resolved.
+  let cancelHelper = false;
+  try {
+    const credentialPromise = navigator.credentials.get(request_options_with_mediation_required());
+    async function helper() {
+      try {
+        if (cancelHelper)
+          return;
+
+        await window.test_driver.select_fedcm_account(0);
+      } catch (ex) {
+        setTimeout(helper, 100);
+      }
+    }
+    helper();
+    const cred = await credentialPromise;
+    await set_fedcm_cookie(manifest_origin);
+    // Now that we have a get(), attempt to revoke permission.
+    attemptRevoke();
+  } catch (error) {
+    window.top.postMessage({result: "Failed get", errorType: error.name}, '*');
+  }
+  // In case the get() call fails and no accounts may be selected, force the
+  // helper function to stop calling itself.
+  cancelHelper = true;
+};
+</script>
+
diff --git a/lint.ignore b/lint.ignore
index 9755dc5..15417b7 100644
--- a/lint.ignore
+++ b/lint.ignore
@@ -294,6 +294,7 @@
 SET TIMEOUT: xhr/resources/xmlhttprequest-timeout.js
 SET TIMEOUT: fenced-frame/resolve-to-config-promise.https.html
 SET TIMEOUT: credential-management/support/fedcm-iframe.html
+SET TIMEOUT: credential-management/support/fedcm/revoke-iframe.html
 
 # generate_tests implementation and sample usage
 GENERATE_TESTS: resources/test/tests/functional/generate-callback.html