Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Digital Credentials: implement visibility and focus requirements
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=275782
rdar://130821648

Reviewed by NOBODY (OOPS!).

Adds visibility, focus, and user activation checks as per:
See WICG/digital-credentials#129

* LayoutTests/http/wpt/identity/identitycredentialscontainer-get-basics.https.html:
* LayoutTests/http/wpt/identity/identitycredentialscontainer-get-hidden.https-expected.txt: Added.
* LayoutTests/http/wpt/identity/identitycredentialscontainer-get-hidden.https.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/digital-credentials/get-user-activation.https-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/digital-credentials/get-user-activation.https.html: Added.
* Source/WebCore/Modules/credentialmanagement/CredentialsContainer.h:
* Source/WebCore/Modules/identity/IdentityCredentialsContainer.cpp:
(WebCore::IdentityCredentialsContainer::get):
marcoscaceres committed Jul 4, 2024
1 parent e5a7357 commit 3d8cd34
Showing 7 changed files with 103 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,44 +1,54 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Digital Credential API: get() default behavior checks.</title>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body></body>
<script>
promise_test(async (t) => {
await test_driver.bless();
await promise_rejects_dom(
t,
"NotSupportedError",
navigator.identity.get(),
"navigator.identity.get() with no argument."
);

await test_driver.bless();
await promise_rejects_dom(
t,
"NotSupportedError",
navigator.identity.get({}),
"navigator.identity.get() with empty dictionary."
);

await test_driver.bless();
await promise_rejects_js(
t,
TypeError,
navigator.identity.get({ digital: "wrong type" }),
"navigator.identity.get() with bogus digital type"
);

await test_driver.bless();
await promise_rejects_dom(
t,
"NotSupportedError",
navigator.identity.get({ bogus_key: "bogus" }),
"navigator.identity.get() with unknown key (same as passing empty dictionary)."
);

await test_driver.bless();
await promise_rejects_js(
t,
TypeError,
navigator.identity.get({ digital: { providers: [] } }),
"navigator.identity.get() with an empty list of providers"
);

await test_driver.bless();
await promise_rejects_js(
t,
TypeError,
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

PASS navigator.identity.get() can't be used with hidden documents.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>
Digital Credential API: get() can't be used with hidden documents.
</title>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body></body>
<script>
promise_test(async (test) => {
await new Promise((resolve) => {
assert_equals(document.visibilityState, "visible", "initially visible");
document.onvisibilitychange = resolve;
testRunner.setPageVisibility("hidden");
});

assert_equals(document.visibilityState, "hidden", "now should be hidden");

await test_driver.bless();

assert_true(
navigator.userActivation.isActive,
"User activation should be active after test_driver.bless()."
);

await promise_rejects_dom(
test,
"NotAllowedError",
navigator.identity.get({})
);
}, "navigator.identity.get() can't be used with hidden documents.");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

PASS navigator.identity.get() calling the API without user activation should reject with NotAllowedError.
PASS navigator.identity.get() consumes user activation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<title>Digital Credential API: get() consumes user activation.</title>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body></body>
<script>
promise_test(async (t) => {
assert_false(
navigator.userActivation.isActive,
"User activation should not be active"
);
await promise_rejects_dom(t, "NotAllowedError", navigator.identity.get({}));
}, "navigator.identity.get() calling the API without user activation should reject with NotAllowedError.");

promise_test(async (t) => {
await test_driver.bless();
assert_true(
navigator.userActivation.isActive,
"User activation should be active after test_driver.bless()."
);
await promise_rejects_dom(
t,
"NotSupportedError",
navigator.identity.get({})
);
assert_false(
navigator.userActivation.isActive,
"User activation should be consumed after navigator.identity.get()."
);
}, "navigator.identity.get() consumes user activation.");
</script>
Original file line number Diff line number Diff line change
@@ -66,9 +66,8 @@ class CredentialsContainer : public RefCounted<CredentialsContainer> {
private:
ScopeAndCrossOriginParent scopeAndCrossOriginParent() const;

WeakPtr<Document, WeakPtrImplWithEventTargetData> m_document;

protected:
WeakPtr<Document, WeakPtrImplWithEventTargetData> m_document;
template<typename Options>
bool performCommonChecks(const Options&, CredentialPromise&);
};
18 changes: 18 additions & 0 deletions Source/WebCore/Modules/identity/IdentityCredentialsContainer.cpp
Original file line number Diff line number Diff line change
@@ -36,6 +36,8 @@
#include "ExceptionOr.h"
#include "JSDOMPromiseDeferred.h"
#include "JSDigitalCredential.h"
#include "LocalDOMWindow.h"
#include "VisibilityState.h"

namespace WebCore {
IdentityCredentialsContainer::IdentityCredentialsContainer(WeakPtr<Document, WeakPtrImplWithEventTargetData>&& document)
@@ -48,6 +50,22 @@ void IdentityCredentialsContainer::get(CredentialRequestOptions&& options, Crede
if (!performCommonChecks(options, promise))
return;

if (!m_document->hasFocus()) {
promise.reject(Exception { ExceptionCode::NotAllowedError, "The document is not focused."_s });
return;
}

if (m_document->visibilityState() != VisibilityState::Visible) {
promise.reject(Exception { ExceptionCode::NotAllowedError, "The document is not visible."_s });
return;
}

RefPtr window = m_document.get()->domWindow();
if (!window || !window->consumeTransientActivation()) {
promise.reject(Exception { ExceptionCode::NotAllowedError, "Calling get() needs to be triggered by an activation triggering user event."_s });
return;
}

if (!options.digital) {
promise.reject(Exception { ExceptionCode::NotSupportedError, "Only digital member is supported."_s });
return;

0 comments on commit 3d8cd34

Please sign in to comment.