Check publickey security requirements for calls to get(publicKey)
and handle empty or missing rpIds.

Add layout tests for various combinations of origins and rpId to
get(publicKey) just like the existing ones for create(publicKey).

Bug: 807774, 664630
Change-Id: I86d3d36c3f3825743f003da69245c74bdca10d5b
Reviewed-on: https://chromium-review.googlesource.com/896384
Commit-Queue: Kim Paulhamus <kpaulhamus@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Balazs Engedy <engedy@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#537594}(cherry picked from commit d1eeb248ea7f29e21901ad3ee3e5b7abcd0d6c18)
Reviewed-on: https://chromium-review.googlesource.com/935223
Reviewed-by: Kim Paulhamus <kpaulhamus@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#578}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index af70d03..35f6ce66 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -647,5 +647,10 @@
     "prefix": "navigation-mojo-response",
     "base": "external/wpt/service-workers/service-worker",
     "args": ["--enable-features=NavigationMojoResponse"]
+  },
+  {
+    "prefix": "enable-webauthn",
+    "base": "http/tests/credentialmanager/",
+    "args": ["--enable-features=WebAuthentication"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
index 7e6b02c..46fef25 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-basics.html
@@ -8,6 +8,9 @@
 <script src="resources/credential-helpers.js"></script>
 <script>
 
+if (document.location.hostname == "127.0.0.1")
+document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-create-basics.html";
+
 promise_test(t => {
     return promise_rejects(t, "NotSupportedError",
         navigator.credentials.create());
@@ -21,13 +24,6 @@
 }, "Verify that invalid domain error returned by mock is properly handled.");
 
 promise_test(t => {
-    mockAuthenticator.setAuthenticatorStatus(
-        webauth.mojom.AuthenticatorStatus.INSECURE_ORIGIN);
-    return promise_rejects(t, "SecurityError",
-        navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that insecure origin error returned by mock is properly handled.");
-
-promise_test(t => {
     var customMakeCredOptions = {
         // No challenge.
         rp: PUBLIC_KEY_RP,
@@ -137,20 +133,73 @@
             navigator.credentials.create({publicKey: customMakeCredOptions}));
 }, "navigator.credentials.create() with missing user.displayName");
 
+promise_test(_ => {
+  mockAuthenticator.setRawId(RAW_ID);
+  mockAuthenticator.setId(ID);
+  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
+  mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
+  mockAuthenticator.setAuthenticatorStatus(
+    webauth.mojom.AuthenticatorStatus.SUCCESS);
+
+    return navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}).then(r => {
+        assertValidMakeCredentialResponse(r);
+    });
+}, "Verify that the mock returns the values we give it.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.PENDING_REQUEST);
+  return promise_rejects(t, "InvalidStateError",
+    navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
+}, "Verify that pending request error returned by mock is properly handled.");
+
 promise_test(function (t) {
-    return promise_rejects(t, "NotSupportedError",
-        navigator.credentials.create());
-}, "navigator.credentials.get() with no argument.");
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.UNKNOWN_ERROR);
+  return promise_rejects(t, "NotReadableError",
+      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
+}, "Verify that unknown error returned by mock is properly handled.");
 
-promise_test(function(t) {
-    var customGetAssertionOptions = {
-        // No challenge.
-        rpId: "google.com",
-        allowCredentials: [ACCEPTABLE_CREDENTIAL]
-    };
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.NOT_ALLOWED_ERROR);
+  return promise_rejects(t, "NotAllowedError",
+      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
+}, "Verify that not allowed error returned by mock is properly handled.");
 
-    return promise_rejects(t, new TypeError(),
-            navigator.credentials.get({publicKey: customGetAssertionOptions}));
-}, "navigator.credentials.get() with missing challenge");
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.NOT_SUPPORTED_ERROR);
+  return promise_rejects(t, "NotSupportedError",
+      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
+}, "Verify that not supported error returned by mock is properly handled.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.TIMED_OUT);
+  return promise_rejects(t, "NotAllowedError",
+      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
+}, "Verify that timed out error returned by mock is properly handled.");
+
+promise_test(_ => {
+  mockAuthenticator.reset();
+  mockAuthenticator.setRawId(RAW_ID);
+  mockAuthenticator.setId(ID);
+  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
+  mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.SUCCESS);
+
+  var customPublicKey = {
+      challenge: CHALLENGE,
+      rp: { name: "Acme" },
+      user: PUBLIC_KEY_USER,
+      pubKeyCredParams: PUBLIC_KEY_PARAMETERS,
+  };
+
+    return navigator.credentials.create({publicKey: customPublicKey}).then(r => {
+        assertValidMakeCredentialResponse(r);
+    });
+}, "navigator.credentials.create() with missing rp.id");
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt
new file mode 100644
index 0000000..924331a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt
@@ -0,0 +1,14 @@
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+This is a testharness.js-based test.
+PASS navigator.credentials should be undefined in documents generated from `data:` URLs.
+PASS navigator.credentials.create({publicKey}) in a javascript url should should succeed.
+PASS navigator.credentials.create({publicKey}) in srcdoc should succeed.
+PASS navigator.credentials.create({publicKey}) in about:blank embedded in a secure context should succeed.
+FAIL navigator.credentials.create({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks. assert_equals: expected "NotAllowedError" but got "NotSupportedError"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame.html
index 5631ca9..eb17e57 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame.html
@@ -12,10 +12,6 @@
 if (document.location.hostname == "127.0.0.1")
 document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-create-from-nested-frame.html";
 
-function EncloseInScriptTag(code) {
-  return "<script>" + code + "</scr" + "ipt>";
-}
-
 promise_test(t => {
   let PROBE_CREDENTIALS = "window.parent.postMessage(String(navigator.credentials), '*');";
 
@@ -44,12 +40,13 @@
   });
 }, "navigator.credentials.create({publicKey}) in a javascript url should should succeed.");
 
-// Load the content we want to inject into the nested frames below into 
+// Load the content we want to inject into the nested frames below into
 // |templateFrame| so that we don't have to use string literals here.
 var templateFrame = document.createElement("iframe");
 templateFrame.src = "resources/nested-mock-authenticator-client.html";
 templateFrame.addEventListener("load", _ => {
 
+  // Uses mockAuthenticator.
   promise_test(t => {
     let frame = document.createElement("iframe");
     frame.srcdoc = templateFrame.contentDocument.documentElement.outerHTML;
@@ -66,6 +63,7 @@
     });
   }, "navigator.credentials.create({publicKey}) in srcdoc should succeed.");
 
+  // Uses mockAuthenticator.
   promise_test(t => {
     let frame = document.createElement("iframe");
     frame.src = "about:blank";
@@ -83,6 +81,7 @@
     });
   }, "navigator.credentials.create({publicKey}) in about:blank embedded in a secure context should succeed.");
 
+  // Does not use mockAuthenticator, but times out instead.
   promise_test(t => {
     let frame = document.createElement("iframe");
     frame.src = "about:blank";
@@ -95,7 +94,7 @@
 
     let eventWatcher = new EventWatcher(t, window, "message");
     return eventWatcher.wait_for("message").then(message => {
-      assert_equals(message.data, "NotSupportedError");
+      assert_equals(message.data, "NotAllowedError");
     });
   }, "navigator.credentials.create({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks.");
 });
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-origins.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-origins.html
index 8a71a98..8a62a9b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-origins.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-create-origins.html
@@ -8,73 +8,8 @@
 <script src="resources/credential-helpers.js"></script>
 <script>
 
-// For tests that don't require custom-set origins.
-if (document.location.hostname == "127.0.0.1")
-document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-create-origins.html";
-
-promise_test(_ => {
-  mockAuthenticator.setRawId(RAW_ID);
-  mockAuthenticator.setId(ID);
-  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
-  mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
-  mockAuthenticator.setAuthenticatorStatus(
-    webauth.mojom.AuthenticatorStatus.SUCCESS);
-
-    return navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}).then(r => {
-        assertValidMakeCredentialResponse(r);
-    });
-}, "Verify that the mock returns the values we give it.");
-
-promise_test(t => {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.PENDING_REQUEST);
-  return promise_rejects(t, "InvalidStateError",
-    navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that pending request error returned by mock is properly handled.");
-
-promise_test(function (t) {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.UNKNOWN_ERROR);
-  return promise_rejects(t, "NotReadableError",
-      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that unknown error returned by mock is properly handled.");
-
-promise_test(t => {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.NOT_ALLOWED_ERROR);
-  return promise_rejects(t, "NotAllowedError",
-      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that not allowed error returned by mock is properly handled.");
-
-promise_test(t => {
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.NOT_SUPPORTED_ERROR);
-  return promise_rejects(t, "NotSupportedError",
-      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
-}, "Verify that not supported error returned by mock is properly handled.");
-
-promise_test(_ => {
-  mockAuthenticator.reset();
-  mockAuthenticator.setRawId(RAW_ID);
-  mockAuthenticator.setId(ID);
-  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
-  mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
-  mockAuthenticator.setAuthenticatorStatus(
-      webauth.mojom.AuthenticatorStatus.SUCCESS);
-
-  var customPublicKey = {
-      challenge: CHALLENGE,
-      rp: { name: "Acme" },
-      user: PUBLIC_KEY_USER,
-      pubKeyCredParams: PUBLIC_KEY_PARAMETERS,
-  };
-
-    return navigator.credentials.create({publicKey: customPublicKey}).then(r => {
-        assertValidMakeCredentialResponse(r);
-    });
-}, "navigator.credentials.create() with missing rp.id");
-
 // For tests that require custom-set origins.
+
 const VALID_ORIGIN_RPID_PAIRS = [
     { 'origin': 'https://google.test:8443',
       'rpId': 'google.test' },
@@ -111,7 +46,7 @@
     { 'origin': 'https://google.test:8443',
       'rpId': null },
     { 'origin': 'https://google.test:8443',
-      'rpId': String(0) }, 
+      'rpId': String(0) },
     { 'origin': 'https://google.test:8443',
       'rpId': 'test' },
 ];
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
index 6089e97..a14b0ff 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
@@ -8,6 +8,9 @@
 <script src="resources/credential-helpers.js"></script>
 <script>
 
+if (document.location.hostname == "127.0.0.1")
+document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-get-basics.html";
+
 add_completion_callback(() => {
   mockCredentialManager.reset();
 });
@@ -108,4 +111,84 @@
   });
 }, "Verify that the mock returns the values we give it.");
 
+promise_test(_ => {
+  mockAuthenticator.setRawId(RAW_ID);
+  mockAuthenticator.setId(ID);
+  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
+  mockAuthenticator.setAuthenticatorData(AUTHENTICATOR_DATA);
+  mockAuthenticator.setSignature(SIGNATURE);
+  mockAuthenticator.setAuthenticatorStatus(
+    webauth.mojom.AuthenticatorStatus.SUCCESS);
+
+    return navigator.credentials.get({publicKey : GET_CREDENTIAL_OPTIONS}).then(r => {
+        assertValidGetCredentialResponse(r);
+    });
+}, "Verify that mockAuthenticator returns the values we give it.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.PENDING_REQUEST);
+  return promise_rejects(t, "InvalidStateError",
+    navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
+}, "Verify that pending request error returned by mock is properly handled.");
+
+promise_test(function (t) {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.UNKNOWN_ERROR);
+  return promise_rejects(t, "NotReadableError",
+      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
+}, "Verify that unknown error returned by mock is properly handled.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.NOT_ALLOWED_ERROR);
+  return promise_rejects(t, "NotAllowedError",
+      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
+}, "Verify that not allowed error returned by mock is properly handled.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.NOT_SUPPORTED_ERROR);
+  return promise_rejects(t, "NotSupportedError",
+      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
+}, "Verify that not supported error returned by mock is properly handled.");
+
+promise_test(t => {
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.TIMED_OUT);
+  return promise_rejects(t, "NotAllowedError",
+      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
+}, "Verify that timed out error returned by mock is properly handled.");
+
+promise_test(function(t) {
+    var customGetCredentialOptions = {
+        // No challenge.
+        rpId: "google.com",
+        allowCredentials: [ACCEPTABLE_CREDENTIAL]
+    };
+
+    return promise_rejects(t, new TypeError(),
+            navigator.credentials.get({publicKey: customGetCredentialOptions}));
+}, "navigator.credentials.get() with missing challenge");
+
+promise_test(_ => {
+  mockAuthenticator.reset();
+  mockAuthenticator.setRawId(RAW_ID);
+  mockAuthenticator.setId(ID);
+  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
+  mockAuthenticator.setAuthenticatorData(AUTHENTICATOR_DATA);
+  mockAuthenticator.setSignature(SIGNATURE);
+  mockAuthenticator.setAuthenticatorStatus(
+      webauth.mojom.AuthenticatorStatus.SUCCESS);
+
+  var customPublicKey = {
+      challenge: CHALLENGE,
+      allowCredentials: [ACCEPTABLE_CREDENTIAL]
+  };
+
+  return navigator.credentials.get({publicKey: customPublicKey}).then(r => {
+      assertValidGetCredentialResponse(r);
+  });
+}, "navigator.credentials.get() with missing rpId");
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt
new file mode 100644
index 0000000..69122ca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt
@@ -0,0 +1,14 @@
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+This is a testharness.js-based test.
+PASS navigator.credentials should be undefined in documents generated from `data:` URLs.
+PASS navigator.credentials.get({publicKey}) in a javascript url should should succeed.
+PASS navigator.credentials.get({publicKey}) in srcdoc should succeed.
+PASS navigator.credentials.get({publicKey}) in about:blank embedded in a secure context should succeed.
+FAIL navigator.credentials.get({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks. assert_equals: expected "NotAllowedError" but got "NotSupportedError"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame.html
new file mode 100644
index 0000000..343136fea
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<title>Credential Manager: Call get() across browsing contexts.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom.js"></script>
+<script src="resources/credential-helpers.js"></script>
+<body>
+<script>
+
+if (document.location.hostname == "127.0.0.1")
+document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-get-from-nested-frame.html";
+
+promise_test(t => {
+  let PROBE_CREDENTIALS = "window.parent.postMessage(String(navigator.credentials), '*');";
+
+  let frame = document.createElement("iframe");
+  frame.src = "data:text/html," + EncloseInScriptTag(PROBE_CREDENTIALS);
+  window.setTimeout(_ => document.body.append(frame));
+
+  let eventWatcher = new EventWatcher(t, window, "message");
+  return eventWatcher.wait_for("message").then(message => {
+    assert_equals(message.data, "undefined");
+  });
+}, "navigator.credentials should be undefined in documents generated from `data:` URLs.");
+
+promise_test(t => {
+  let frame = document.createElement("iframe");
+  frame.src = "resources/nested-mock-authenticator-client.html";
+  window.setTimeout(_ => document.body.append(frame));
+
+  let loadWatcher = new EventWatcher(t, frame, "load");
+  loadWatcher.wait_for("load").then(_ =>
+      frame.contentWindow.location = "javascript:" + GET_CREDENTIAL);
+
+  let messageWatcher = new EventWatcher(t, window, "message");
+  return messageWatcher.wait_for("message").then(message => {
+    assert_equals(message.data, TEST_NESTED_CREDENTIAL_ID);
+  });
+}, "navigator.credentials.get({publicKey}) in a javascript url should should succeed.");
+
+// Load the content we want to inject into the nested frames below into
+// |templateFrame| so that we don't have to use string literals here.
+var templateFrame = document.createElement("iframe");
+templateFrame.src = "resources/nested-mock-authenticator-client.html";
+templateFrame.addEventListener("load", _ => {
+
+  // Uses mockAuthenticator.
+  promise_test(t => {
+    let frame = document.createElement("iframe");
+    frame.srcdoc = templateFrame.contentDocument.documentElement.outerHTML;
+    window.setTimeout(_ => document.body.append(frame));
+
+    let loadWatcher = new EventWatcher(t, frame, "load");
+    loadWatcher.wait_for("load").then(_ => {
+      frame.contentWindow.eval(GET_CREDENTIAL);
+    });
+
+    let eventWatcher = new EventWatcher(t, window, "message");
+    return eventWatcher.wait_for("message").then(message => {
+      assert_equals(message.data, TEST_NESTED_CREDENTIAL_ID);
+    });
+  }, "navigator.credentials.get({publicKey}) in srcdoc should succeed.");
+
+  // Uses mockAuthenticator.
+  promise_test(t => {
+    let frame = document.createElement("iframe");
+    frame.src = "about:blank";
+    window.setTimeout(_ => document.body.append(frame));
+
+    let loadWatcher = new EventWatcher(t, frame, "load");
+    loadWatcher.wait_for("load").then(_ => {
+      frame.contentDocument.write(templateFrame.contentDocument.documentElement.outerHTML);
+      frame.contentDocument.write(EncloseInScriptTag(GET_CUSTOM_CREDENTIALS));
+    });
+
+    let eventWatcher = new EventWatcher(t, window, "message");
+    return eventWatcher.wait_for("message").then(message => {
+      assert_equals(message.data, TEST_NESTED_CREDENTIAL_ID);
+    });
+  }, "navigator.credentials.get({publicKey}) in about:blank embedded in a secure context should succeed.");
+
+  // Does not use mockAuthenticator, but times out instead.
+  promise_test(t => {
+    let frame = document.createElement("iframe");
+    frame.src = "about:blank";
+    window.setTimeout(_ => document.body.append(frame));
+
+    let loadWatcher = new EventWatcher(t, frame, "load");
+    loadWatcher.wait_for("load").then(_ => {
+      frame.contentWindow.eval(GET_CUSTOM_CREDENTIALS);
+    });
+
+    let eventWatcher = new EventWatcher(t, window, "message");
+    return eventWatcher.wait_for("message").then(message => {
+      assert_equals(message.data, "NotAllowedError");
+    });
+  }, "navigator.credentials.get({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks.");
+});
+document.body.append(templateFrame);
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-origins.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-origins.html
new file mode 100644
index 0000000..d0dd88d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-origins.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Credential Manager: get() with custom origins.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom.js"></script>
+<script src="resources/credential-helpers.js"></script>
+<script>
+
+// For tests that require custom-set origins.
+
+const VALID_ORIGIN_RPID_PAIRS = [
+    { 'origin': 'https://google.test:8443',
+      'rpId': 'google.test' },
+    { 'origin': 'https://google.test:8443',
+      'rpId': '' },
+    {'origin': 'https://subdomain.example.test:8443',
+      'rpId': 'example.test' },
+    {'origin': 'https://subdomain.example.test:8443',
+      'rpId': 'subdomain.example.test' },
+    {'origin': 'https://localhost:8443',
+      'rpId': 'localhost' },
+];
+
+for (let test of VALID_ORIGIN_RPID_PAIRS) {
+  promise_test(t => {
+    let eventWatcher = new EventWatcher(t, window, "message");
+
+    var w = window.open(test.origin
+        + "/credentialmanager/resources/publickey-get-helper.html?rpId="
+        + test.rpId);
+    return eventWatcher.wait_for("message").then(message => {
+        assert_equals(message.data, "SUCCESS");
+    });
+  }, "navigator.credentials.get({publicKey}) in '" +  test.origin
+      + "' with valid |rpId| '" + test.rpId + "' should succeed.");
+}
+
+const INVALID_ORIGIN_RPID_PAIRS = [
+    { 'origin': 'https://google.test:8443',
+      'rpId': 'localhost' },
+    { 'origin': 'https://google.test:8443',
+      'rpId': 'foo.google.test' },
+    { 'origin': 'https://google.test:8443',
+      'rpId': null },
+    { 'origin': 'https://google.test:8443',
+      'rpId': String(0) },
+    { 'origin': 'https://google.test:8443',
+      'rpId': 'test' },
+];
+
+for (let test of INVALID_ORIGIN_RPID_PAIRS) {
+  promise_test(t => {
+    let eventWatcher = new EventWatcher(t, window, "message");
+
+    var w = window.open(test.origin
+        + "/credentialmanager/resources/publickey-get-helper.html?rpId="
+        + test.rpId);
+
+    return eventWatcher.wait_for("message").then(message => {
+        assert_equals(message.data, "SecurityError");
+    });
+  }, "navigator.credentials.get({publicKey}) in '" +  test.origin
+      + "' with invalid |rpId| '" + test.rpId + "' should fail.");
+}
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/credential-helpers.js b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/credential-helpers.js
index a427365..cbb0161 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/credential-helpers.js
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/credential-helpers.js
@@ -15,12 +15,12 @@
   }
 
   constructCredentialInfo_(type, id, password, name, icon) {
-    return new passwordManager.mojom.CredentialInfo({
+  return new passwordManager.mojom.CredentialInfo({
       type: type,
       id: id,
       name: name,
       icon: new url.mojom.Url({url: icon}),
-      password: password,
+    password: password,
       federation: new url.mojom.Origin(
           {scheme: '', host: '', port: 0, suborigin: '', unique: true})
     });
@@ -198,7 +198,7 @@
     transports: ["usb", "nfc", "ble"]
 };
 
-var GET_ASSERTION_OPTIONS = {
+var GET_CREDENTIAL_OPTIONS = {
     challenge: CHALLENGE,
     rpId: "subdomain.example.test",
     allowCredentials: [ACCEPTABLE_CREDENTIAL]
@@ -213,17 +213,18 @@
 
 var TEST_NESTED_CREDENTIAL_ID = "nestedCredentialId";
 
-// Use an invalid algorithm in the parameters for "success" cases
-// so each test will exercise the rpID checks in  both the renderer
-// and browser but return prior to reaching the device layer.
-var CUSTOM_PUBLIC_KEY = 'var customPublicKey = '
-    + '{challenge : new TextEncoder().encode("challenge"), '
+// Use a 3-second timeout in the parameters for "success" cases
+// so that each test will exercise the rpID checks in both the renderer
+// and browser but will time out instead of wait for a device response.
+var CUSTOM_MAKE_CREDENTIAL_OPTIONS = 'var customPublicKey = '
+    + '{challenge: new TextEncoder().encode("challenge"), '
     + 'rp: {id: "subdomain.example.test", name: "Acme"}, '
     + 'user: {id: new TextEncoder().encode("1098237235409872"), '
     + 'name: "acme@example.com", displayName: "Acme", icon:"iconUrl"}, '
-    + 'pubKeyCredParams: [{type: "public-key", alg: 0,},], excludeCredentials:[],};';
+    + 'timeout: 2000, '
+    + 'pubKeyCredParams: [{type: "public-key", alg: -7,},], excludeCredentials:[],};';
 
-var CREATE_CUSTOM_CREDENTIALS = CUSTOM_PUBLIC_KEY
+var CREATE_CUSTOM_CREDENTIALS = CUSTOM_MAKE_CREDENTIAL_OPTIONS
     + "navigator.credentials.create({publicKey : customPublicKey})"
     + ".then(c => window.parent.postMessage(c.id, '*'))"
     + ".catch(e => window.parent.postMessage(e.name, '*'));";
@@ -231,6 +232,24 @@
 var CREATE_CREDENTIALS = "navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS})"
     + ".then(c => window.parent.postMessage(c.id, '*'));";
 
+var CUSTOM_GET_CREDENTIAL_OPTIONS = 'var customPublicKey = '
+    + '{challenge: new TextEncoder().encode("challenge"), '
+    + 'rpId: "subdomain.example.test", '
+    + 'timeout: 2000, '
+    + 'allowCredentials: [{type: "public-key", id: new TextEncoder().encode("allowedCredential"), transports: ["usb", "nfc", "ble"]},],};';
+
+var GET_CUSTOM_CREDENTIALS = CUSTOM_GET_CREDENTIAL_OPTIONS
+    + "navigator.credentials.get({publicKey : customPublicKey})"
+    + ".then(c => window.parent.postMessage(c.id, '*'))"
+    + ".catch(e => window.parent.postMessage(e.name, '*'));";
+
+var GET_CREDENTIAL = "navigator.credentials.get({publicKey : GET_CREDENTIAL_OPTIONS})"
+    + ".then(c => window.parent.postMessage(c.id, '*'));";
+
+function EncloseInScriptTag(code) {
+  return "<script>" + code + "</scr" + "ipt>";
+}
+
 // Verifies if |r| is the valid response to credentials.create(publicKey).
 function assertValidMakeCredentialResponse(r) {
 assert_equals(r.id, ID, 'id');
@@ -249,7 +268,7 @@
 }
 
 // Verifies if |r| is the valid response to credentials.get(publicKey).
-function assertValidGetAssertionResponse(r) {
+function assertValidGetCredentialResponse(r) {
     assert_equals(r.id, ID, 'id');
     assert_true(r.rawId instanceof ArrayBuffer);
     assert_array_equals(new Uint8Array(r.rawId),
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/nested-mock-authenticator-client.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/nested-mock-authenticator-client.html
index b706f6e..19dc356 100644
--- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/nested-mock-authenticator-client.html
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/nested-mock-authenticator-client.html
@@ -11,6 +11,8 @@
 mockAuthenticator.setId(TEST_NESTED_CREDENTIAL_ID);
 mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
 mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
+mockAuthenticator.setAuthenticatorData(AUTHENTICATOR_DATA);
+mockAuthenticator.setSignature(SIGNATURE);
 mockAuthenticator.setAuthenticatorStatus(
     webauth.mojom.AuthenticatorStatus.SUCCESS);
 </script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/publickey-get-helper.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/publickey-get-helper.html
new file mode 100644
index 0000000..86ff8dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/resources/publickey-get-helper.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.js"></script>
+<script src="/gen/third_party/WebKit/public/platform/modules/webauth/authenticator.mojom.js"></script>
+<script src="credential-helpers.js"></script>
+<script>
+mockAuthenticator.setRawId(RAW_ID);
+mockAuthenticator.setId(ID);
+mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
+mockAuthenticator.setAuthenticatorData(AUTHENTICATOR_DATA);
+mockAuthenticator.setSignature(SIGNATURE);
+mockAuthenticator.setAuthenticatorStatus(
+    webauth.mojom.AuthenticatorStatus.SUCCESS);
+let queryParams = new URLSearchParams(window.location.search);
+let relyingPartyId = queryParams.get('rpId');
+
+var customPublicKey = {
+    challenge: CHALLENGE,
+    rpId: relyingPartyId,
+    allowCredentials: [ACCEPTABLE_CREDENTIAL]
+};
+
+navigator.credentials.get({publicKey : customPublicKey})
+  .then(r => window.opener.postMessage("SUCCESS", "*"))
+  .catch(t => window.opener.postMessage(t.name, "*"));
+</script>
diff --git a/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/README.txt b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/README.txt
new file mode 100644
index 0000000..0b09327
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/README.txt
@@ -0,0 +1,3 @@
+# This suite runs tests that depend on --enable-features=WebAuthentication.
+# This flag enables the authenticator mojom implementation, which is needed
+# for certain tests that cannot use a mock authenticator.
diff --git a/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt
new file mode 100644
index 0000000..29354b75
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-create-from-nested-frame-expected.txt
@@ -0,0 +1,14 @@
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+This is a testharness.js-based test.
+PASS navigator.credentials should be undefined in documents generated from `data:` URLs.
+PASS navigator.credentials.create({publicKey}) in a javascript url should should succeed.
+PASS navigator.credentials.create({publicKey}) in srcdoc should succeed.
+PASS navigator.credentials.create({publicKey}) in about:blank embedded in a secure context should succeed.
+PASS navigator.credentials.create({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt
new file mode 100644
index 0000000..b80afa9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/enable-webauthn/http/tests/credentialmanager/credentialscontainer-get-from-nested-frame-expected.txt
@@ -0,0 +1,14 @@
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/origin.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: url/mojom/url.mojom
+CONSOLE WARNING: line 10: The following mojom is loaded multiple times: mojo/common/time.mojom
+This is a testharness.js-based test.
+PASS navigator.credentials should be undefined in documents generated from `data:` URLs.
+PASS navigator.credentials.get({publicKey}) in a javascript url should should succeed.
+PASS navigator.credentials.get({publicKey}) in srcdoc should succeed.
+PASS navigator.credentials.get({publicKey}) in about:blank embedded in a secure context should succeed.
+PASS navigator.credentials.get({publicKey}) in an about:blank page embedded in a secure context should pass rpID checks.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
index 0f6d75d..114ca88a 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialManagerTypeConverters.cpp
@@ -22,8 +22,8 @@
 
 namespace {
 // Time to wait for an authenticator to successfully complete an operation.
-constexpr TimeDelta kAdjustedTimeoutLower = TimeDelta::FromMinutes(1);
-constexpr TimeDelta kAdjustedTimeoutUpper = TimeDelta::FromMinutes(2);
+constexpr TimeDelta kAdjustedTimeoutLower = TimeDelta::FromSeconds(1);
+constexpr TimeDelta kAdjustedTimeoutUpper = TimeDelta::FromMinutes(1);
 
 WTF::TimeDelta AdjustTimeout(uint32_t timeout) {
   WTF::TimeDelta adjusted_timeout;
@@ -249,7 +249,7 @@
   if (options.hasTimeout()) {
     mojo_options->adjusted_timeout = AdjustTimeout(options.timeout());
   } else {
-    mojo_options->adjusted_timeout = kAdjustedTimeoutLower;
+    mojo_options->adjusted_timeout = kAdjustedTimeoutUpper;
   }
 
   // Steps 8 and 9 of
@@ -294,7 +294,7 @@
   if (options.hasTimeout()) {
     mojo_options->adjusted_timeout = AdjustTimeout(options.timeout());
   } else {
-    mojo_options->adjusted_timeout = kAdjustedTimeoutLower;
+    mojo_options->adjusted_timeout = kAdjustedTimeoutUpper;
   }
 
   mojo_options->relying_party_id = options.rpId();
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
index 0c36b74..e418e1c 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
@@ -69,7 +69,7 @@
   }
 
   // Releases the owned |resolver_|. This is to be called by the Mojo response
-  // callback responsible for resolving the corresponding ScriptPromise.
+  // callback responsible for resolving the corresponding ScriptPromise
   //
   // If this method is not called before |this| goes of scope, it is assumed
   // that a Mojo connection error has occurred, and the response callback was
@@ -391,9 +391,19 @@
     return promise;
 
   if (options.hasPublicKey()) {
+    const String& relying_party_id = options.publicKey().rpId();
+    if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id))
+      return promise;
+
     auto mojo_options =
         MojoPublicKeyCredentialRequestOptions::From(options.publicKey());
     if (mojo_options) {
+      if (!mojo_options->relying_party_id) {
+        mojo_options->relying_party_id = resolver->GetFrame()
+                                             ->GetSecurityContext()
+                                             ->GetSecurityOrigin()
+                                             ->Domain();
+      }
       auto* authenticator =
           CredentialManagerProxy::From(script_state)->Authenticator();
       authenticator->GetAssertion(
@@ -515,9 +525,9 @@
   } else {
     DCHECK(options.hasPublicKey());
     const String& relying_party_id = options.publicKey().rp().id();
-    if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id)) {
+    if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id))
       return promise;
-    }
+
     auto mojo_options =
         MojoMakePublicKeyCredentialOptions::From(options.publicKey());
     if (mojo_options) {