commit 14e523cf736d0c165daa2bf4165515f224f99e3d
parent 6686939edd3e8e1e74bc6e89dddf1afe10046970
Author: Marcos Cáceres <caceres_m@apple.com>
Date: Thu, 4 Dec 2025 07:10:04 +0000
Bug 2003206 [wpt-sync] Sync PR 56350 - Digital Credentials: add `signal` support to config property bag, a=testonly
- Add signal parameter to MakeGetOptionsConfig and MakeCreateOptionsConfig interfaces
- Update CredentialRequestOptions and CredentialCreationOptions to include optional signal
- Modify helper functions to accept and pass through abort signal
- Update test files to use new signal property in config objects
- Enables cleaner abort signal handling: makeGetOptions({protocol: 'x', signal})
wpt-commit: f57eba26d024a27e1dce2e656aad59c9e2b1f54b
wpt-type: dependency
Diffstat:
5 files changed, 44 insertions(+), 23 deletions(-)
diff --git a/testing/web-platform/tests/digital-credentials/create.tentative.https.html b/testing/web-platform/tests/digital-credentials/create.tentative.https.html
@@ -136,7 +136,7 @@
const abortController = new AbortController();
const { signal } = abortController;
abortController.abort();
- for (const options of [{ signal }, { signal, ...makeCreateOptions({protocol: []}) }]) {
+ for (const options of [{ signal }, makeCreateOptions({protocol: [], signal})]) {
await promise_rejects_dom(
t,
"AbortError",
@@ -151,7 +151,7 @@
const abortController = new iframeWindow.AbortController();
const { signal } = abortController;
abortController.abort();
- for (const options of [{ signal }, { signal, ...makeCreateOptions({protocol: []}) }]) {
+ for (const options of [{ signal }, makeCreateOptions({protocol: [], signal})]) {
await test_driver.bless("user activation");
await promise_rejects_dom(
t,
@@ -182,8 +182,7 @@
promise_test(async (t) => {
const abortController = new AbortController();
const { signal } = abortController;
- const options = makeCreateOptions({protocol: "openid4vci"});
- options.signal = signal;
+ const options = makeCreateOptions({ protocol: "openid4vci", signal });
await test_driver.bless("user activation");
const promise = promise_rejects_dom(
t,
diff --git a/testing/web-platform/tests/digital-credentials/dc-types.ts b/testing/web-platform/tests/digital-credentials/dc-types.ts
@@ -19,6 +19,10 @@ export interface MakeGetOptionsConfig {
* Credential mediation requirement
*/
mediation?: CredentialMediationRequirement;
+ /**
+ * Optional AbortSignal for request cancellation
+ */
+ signal?: AbortSignal;
}
/**
@@ -33,6 +37,10 @@ export interface MakeCreateOptionsConfig {
* Credential mediation requirement
*/
mediation?: CredentialMediationRequirement;
+ /**
+ * Optional AbortSignal for request cancellation
+ */
+ signal?: AbortSignal;
}
/**
@@ -59,6 +67,7 @@ export interface DigitalCredentialRequestOptions {
export interface CredentialRequestOptions {
digital: DigitalCredentialRequestOptions;
mediation: CredentialMediationRequirement;
+ signal?: AbortSignal;
}
/**
@@ -85,6 +94,7 @@ export interface DigitalCredentialCreationOptions {
export interface CredentialCreationOptions {
digital: DigitalCredentialCreationOptions;
mediation: CredentialMediationRequirement;
+ signal?: AbortSignal;
}
/**
diff --git a/testing/web-platform/tests/digital-credentials/get.tentative.https.html b/testing/web-platform/tests/digital-credentials/get.tentative.https.html
@@ -160,7 +160,7 @@
const abortController = new AbortController();
const { signal } = abortController;
abortController.abort();
- for (const options of [{ signal }, { signal, ...makeGetOptions({protocol: []}) }]) {
+ for (const options of [{ signal }, makeGetOptions({protocol: [], signal})]) {
await promise_rejects_dom(
t,
"AbortError",
@@ -175,7 +175,7 @@
const abortController = new iframeWindow.AbortController();
const { signal } = abortController;
abortController.abort();
- for (const options of [{ signal }, { signal, ...makeGetOptions({protocol: []}) }]) {
+ for (const options of [{ signal }, makeGetOptions({protocol: [], signal})]) {
await test_driver.bless("user activation");
await promise_rejects_dom(
t,
@@ -206,8 +206,7 @@
promise_test(async (t) => {
const abortController = new AbortController();
const { signal } = abortController;
- const options = makeGetOptions({protocol: "openid4vp-v1-unsigned"});
- options.signal = signal;
+ const options = makeGetOptions({protocol: "openid4vp-v1-unsigned", signal});
await test_driver.bless("user activation");
const promise = promise_rejects_dom(
t,
diff --git a/testing/web-platform/tests/digital-credentials/support/helper.js b/testing/web-platform/tests/digital-credentials/support/helper.js
@@ -22,10 +22,11 @@
* @param {string[]} requestsInputArray - An array of request type strings.
* @param {CredentialMediationRequirement} mediation - The mediation requirement.
* @param {Record<string, () => any>} requestMapping - The specific mapping object for the operation type.
- * @returns {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement }} - The final options structure.
+ * @param {AbortSignal} [signal] - Optional abort signal.
+ * @returns {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement, signal?: AbortSignal }} - The final options structure.
* @throws {Error} If an unknown request type string is encountered within the array.
*/
-function _makeOptionsInternal(requestsInputArray, mediation, requestMapping) {
+function _makeOptionsInternal(requestsInputArray, mediation, requestMapping, signal) {
const requests = [];
for (const request of requestsInputArray) {
const factoryFunction = requestMapping[request];
@@ -36,7 +37,11 @@ function _makeOptionsInternal(requestsInputArray, mediation, requestMapping) {
throw new Error(`Unknown request type within array: ${request}`);
}
}
- return { digital: { requests }, mediation };
+ const result = { digital: { requests }, mediation };
+ if (signal !== undefined) {
+ result.signal = signal;
+ }
+ return result;
}
const allMappings = {
@@ -59,10 +64,11 @@ const allMappings = {
* @param {'get' | 'create'} type - The type of operation.
* @param {string | string[]} protocol - Protocol(s) to use.
* @param {CredentialMediationRequirement} mediation - Mediation requirement.
- * @returns {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement }}
+ * @param {AbortSignal} [signal] - Optional abort signal.
+ * @returns {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement, signal?: AbortSignal }}
* @throws {Error} If type is invalid internally, or input strings are invalid.
*/
-function _makeOptionsUnified(type, protocol, mediation) {
+function _makeOptionsUnified(type, protocol, mediation, signal) {
// 1. Get mapping (Type validation primarily happens via caller)
const mapping = allMappings[type];
// Added safety check, though public functions should prevent this.
@@ -74,7 +80,7 @@ function _makeOptionsUnified(type, protocol, mediation) {
if (typeof protocol === 'string') {
if (protocol in mapping) {
// Valid single string: Pass as array to the core array helper
- return _makeOptionsInternal([protocol], mediation, mapping);
+ return _makeOptionsInternal([protocol], mediation, mapping, signal);
} else {
// Invalid single string for this type
throw new Error(`Unknown request type string '${protocol}' provided for operation type '${type}'`);
@@ -85,14 +91,22 @@ function _makeOptionsUnified(type, protocol, mediation) {
if (Array.isArray(protocol)) {
if (protocol.length === 0) {
// Handle empty array explicitly
- return { digital: { requests: [] }, mediation };
+ const result = { digital: { requests: [] }, mediation };
+ if (signal !== undefined) {
+ result.signal = signal;
+ }
+ return result;
}
// Pass valid non-empty array to the core array helper
- return _makeOptionsInternal(protocol, mediation, mapping);
+ return _makeOptionsInternal(protocol, mediation, mapping, signal);
}
// 4. Handle invalid input types (neither string nor array)
- return { digital: { requests: [] }, mediation };
+ const result = { digital: { requests: [] }, mediation };
+ if (signal !== undefined) {
+ result.signal = signal;
+ }
+ return result;
}
/**
@@ -102,8 +116,8 @@ function _makeOptionsUnified(type, protocol, mediation) {
* @returns {CredentialRequestOptions}
*/
export function makeGetOptions(config = {}) {
- const { protocol = "default", mediation = "required" } = config;
- return _makeOptionsUnified('get', protocol, mediation);
+ const { protocol = "default", mediation = "required", signal } = config;
+ return _makeOptionsUnified('get', protocol, mediation, signal);
}
/**
@@ -113,8 +127,8 @@ export function makeGetOptions(config = {}) {
* @returns {CredentialCreationOptions}
*/
export function makeCreateOptions(config = {}) {
- const { protocol = "default", mediation = "required" } = config;
- return _makeOptionsUnified('create', protocol, mediation);
+ const { protocol = "default", mediation = "required", signal } = config;
+ return _makeOptionsUnified('create', protocol, mediation, signal);
}
/**
diff --git a/testing/web-platform/tests/digital-credentials/user-activation.https.html b/testing/web-platform/tests/digital-credentials/user-activation.https.html
@@ -25,8 +25,7 @@
promise_test(async (t) => {
await test_driver.bless();
const abort = new AbortController();
- const options = makeGetOptions({protocol: "openid4vp-v1-unsigned"});
- options.signal = abort.signal;
+ const options = makeGetOptions({protocol: "openid4vp-v1-unsigned", signal: abort.signal});
assert_true(
navigator.userActivation.isActive,
"User activation should be active after test_driver.bless()."