From 27325969e5e1be3b3b188d5ab5a1b1776045a23f Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 23 Apr 2025 08:47:30 +0000 Subject: [PATCH 1/6] add naive polyfill --- .../src/server/webkit/wkBrowser.ts | 22 +++++++++++++++++++ tests/library/modernizr.spec.ts | 2 -- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index 5af64c8bb412d..7208fa36c8125 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -223,6 +223,7 @@ export class WKBrowserContext extends BrowserContext { downloadPath: this._browser.options.downloadsPath, browserContextId })); + promises.push(this._polyfillPublicKeyCredential()); if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) promises.push(this._browser._browserSession.send('Playwright.setIgnoreCertificateErrors', { browserContextId, ignore: true })); if (this._options.locale) @@ -357,4 +358,25 @@ export class WKBrowserContext extends BrowserContext { if (process.platform === 'win32' && this._browser.options.headful && (viewportSize.width < 250 || viewportSize.height < 240)) throw new Error(`WebKit on Windows has a minimal viewport of 250x240.`); } + + private async _polyfillPublicKeyCredential() { + if (process.platform === 'darwin' || process.platform === 'win32') + return; + + function polyfill() { + globalThis.PublicKeyCredential ??= { + async getClientCapabilities() { + return {}; + }, + async isConditionalMediationAvailable() { + return false; + }, + async isUserVerifyingPlatformAuthenticatorAvailable() { + return false; + }, + } as any; + } + + await this.addInitScript(`(${polyfill})()`); + } } diff --git a/tests/library/modernizr.spec.ts b/tests/library/modernizr.spec.ts index 8777b3f7dbb77..3c4edb141e691 100644 --- a/tests/library/modernizr.spec.ts +++ b/tests/library/modernizr.spec.ts @@ -55,7 +55,6 @@ it('Safari Desktop', async ({ browser, browserName, platform, server, headless } if (platform === 'linux') { expected.speechrecognition = false; - expected.publickeycredential = false; expected.mediastream = false; if (headless) expected.todataurlwebp = true; @@ -118,7 +117,6 @@ it('Mobile Safari', async ({ playwright, browser, browserName, platform, server, if (platform === 'linux') { expected.speechrecognition = false; - expected.publickeycredential = false; expected.mediastream = false; if (headless) expected.todataurlwebp = true; From 6cce1b70a10cd877b1a5bb4e0a2b28ced378be5f Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 23 Apr 2025 08:54:58 +0000 Subject: [PATCH 2/6] add test for feature detection --- tests/library/capabilities.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/library/capabilities.spec.ts b/tests/library/capabilities.spec.ts index 8b065834ab2b0..23cb4ac3991c7 100644 --- a/tests/library/capabilities.spec.ts +++ b/tests/library/capabilities.spec.ts @@ -480,3 +480,17 @@ it('should not auto play audio', { await page.goto('http://127.0.0.1/audio.html'); await expect(page.locator('#log')).toHaveText('State: suspended'); }); + +it('should not crash on feature detection for PublicKeyCredential', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/35433' + } +}, async ({ page, server }) => { + await page.goto(server.EMPTY_PAGE); + await page.evaluate(async () => { + await PublicKeyCredential.getClientCapabilities(); + await PublicKeyCredential.isConditionalMediationAvailable(); + await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + }); +}); From ebc35adae9cb56b49db2d5420e2c367093e8071a Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 23 Apr 2025 08:57:52 +0000 Subject: [PATCH 3/6] add comment --- packages/playwright-core/src/server/webkit/wkBrowser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index 7208fa36c8125..a491350a8891d 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -364,6 +364,7 @@ export class WKBrowserContext extends BrowserContext { return; function polyfill() { + // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential globalThis.PublicKeyCredential ??= { async getClientCapabilities() { return {}; From 3d2136f2c07a7a4dd33d0ffe53ebcc57c89fa7f5 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Fri, 2 May 2025 11:41:33 +0200 Subject: [PATCH 4/6] move to _calculateBootstrapScript --- .../src/server/webkit/wkBrowser.ts | 23 ------------------- .../src/server/webkit/wkPage.ts | 19 +++++++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/playwright-core/src/server/webkit/wkBrowser.ts b/packages/playwright-core/src/server/webkit/wkBrowser.ts index a491350a8891d..5af64c8bb412d 100644 --- a/packages/playwright-core/src/server/webkit/wkBrowser.ts +++ b/packages/playwright-core/src/server/webkit/wkBrowser.ts @@ -223,7 +223,6 @@ export class WKBrowserContext extends BrowserContext { downloadPath: this._browser.options.downloadsPath, browserContextId })); - promises.push(this._polyfillPublicKeyCredential()); if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) promises.push(this._browser._browserSession.send('Playwright.setIgnoreCertificateErrors', { browserContextId, ignore: true })); if (this._options.locale) @@ -358,26 +357,4 @@ export class WKBrowserContext extends BrowserContext { if (process.platform === 'win32' && this._browser.options.headful && (viewportSize.width < 250 || viewportSize.height < 240)) throw new Error(`WebKit on Windows has a minimal viewport of 250x240.`); } - - private async _polyfillPublicKeyCredential() { - if (process.platform === 'darwin' || process.platform === 'win32') - return; - - function polyfill() { - // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential - globalThis.PublicKeyCredential ??= { - async getClientCapabilities() { - return {}; - }, - async isConditionalMediationAvailable() { - return false; - }, - async isUserVerifyingPlatformAuthenticatorAvailable() { - return false; - }, - } as any; - } - - await this.addInitScript(`(${polyfill})()`); - } } diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 7737c5163f61c..0afdb825d9d90 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -781,10 +781,29 @@ export class WKPage implements PageDelegate { } scripts.push('if (!window.safari) window.safari = { pushNotification: { toString() { return "[object SafariRemoteNotification]"; } } };'); scripts.push('if (!window.GestureEvent) window.GestureEvent = function GestureEvent() {};'); + scripts.push(this._publicKeyCredentialScript()); scripts.push(...this._page.allInitScripts().map(script => script.source)); return scripts.join(';\n'); } + private _publicKeyCredentialScript(): string { + function polyfill() { + // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential + globalThis.PublicKeyCredential ??= { + async getClientCapabilities() { + return {}; + }, + async isConditionalMediationAvailable() { + return false; + }, + async isUserVerifyingPlatformAuthenticatorAvailable() { + return false; + }, + } as any; + } + return `(${polyfill.toString()})();`; + } + async _updateBootstrapScript(): Promise { await this._updateState('Page.setBootstrapScript', { source: this._calculateBootstrapScript() }); } From df17fa531d4c056e72f4e2c3633582ad9b17eebe Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Fri, 2 May 2025 09:50:17 +0000 Subject: [PATCH 5/6] add comment --- packages/playwright-core/src/server/webkit/wkPage.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 0afdb825d9d90..761a33be022cc 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -788,8 +788,12 @@ export class WKPage implements PageDelegate { private _publicKeyCredentialScript(): string { function polyfill() { - // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential - globalThis.PublicKeyCredential ??= { + /** + * Some sites don't check existance of PublicKeyCredentials because all browsers except Webkit on Linux implement it. + * We polyfill the subset that's used for feature detection, so that login flows that'd work in Safari don't crash with "PublicKeyCredential is not defined" in CI. + * https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential + */ + window.PublicKeyCredential ??= { async getClientCapabilities() { return {}; }, From de60314265b220271738e7784cc549d3526fa8d6 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Fri, 2 May 2025 13:54:08 +0200 Subject: [PATCH 6/6] lint --- packages/playwright-core/src/server/webkit/wkPage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 761a33be022cc..3261ff40c98bc 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -792,7 +792,7 @@ export class WKPage implements PageDelegate { * Some sites don't check existance of PublicKeyCredentials because all browsers except Webkit on Linux implement it. * We polyfill the subset that's used for feature detection, so that login flows that'd work in Safari don't crash with "PublicKeyCredential is not defined" in CI. * https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential - */ + */ window.PublicKeyCredential ??= { async getClientCapabilities() { return {};