From cc45753c6ced902a95fcbee989baca5b8776816d Mon Sep 17 00:00:00 2001 From: Devin Rousso Date: Thu, 13 Aug 2026 10:37:23 -0600 Subject: [PATCH] test(clipboard): check that the headless clipboard is isolated from the operating system (#42039) headless WebKit has started using the operating system clipboard in builds `2334` and `2345`, allowing browser instances to read clipboard contents from outside the process add a test that launches two headless browsers and expects each to read only what it wrote through `navigator.clipboard` fixes --- tests/library/permissions.spec.ts | 35 ++++++++++++++++++++++++++++--- tests/mcp/clipboard.spec.ts | 5 +++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/library/permissions.spec.ts b/tests/library/permissions.spec.ts index d66340f46e85d..64a414296aa7a 100644 --- a/tests/library/permissions.spec.ts +++ b/tests/library/permissions.spec.ts @@ -192,11 +192,9 @@ it.describe('permissions', () => { }); }); -it('should support clipboard read', async ({ page, context, server, browserName, isWindows, isLinux, headless, isHeadlessShell }) => { +it('should support clipboard read', async ({ page, context, server, browserName, isWindows, isHeadlessShell }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/27475' }); it.fail(browserName === 'firefox', 'No such permissions (requires flag) in Firefox'); - it.fixme(browserName === 'webkit' && isWindows, 'WebPasteboardProxy::allPasteboardItemInfo not implemented for Windows.'); - it.fixme(browserName === 'webkit' && isLinux && headless, 'WebPasteboardProxy::allPasteboardItemInfo not implemented for WPE.'); await page.goto(server.EMPTY_PAGE); // There is no 'clipboard-read' permission in WebKit Web API. @@ -219,6 +217,37 @@ it('should support clipboard read', async ({ page, context, server, browserName, expect(await page.evaluate(() => navigator.clipboard.readText())).toBe('test content'); }); +it('should isolate the headless clipboard from the operating system', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13097' }, +}, async ({ browserType, server, browserName, isFrozenWebkit }) => { + it.skip(isFrozenWebkit, 'needs recent webkit'); + + // Each headless browser gets its own clipboard, so neither can see the clipboard of the operating system nor of another browser. + const browser1 = await browserType.launch({ headless: true }); + const browser2 = await browserType.launch({ headless: true }); + const [page1, page2] = await Promise.all([browser1, browser2].map(async browser => { + const context = await browser.newContext(); + // There is no 'clipboard-write' permission in WebKit Web API and no clipboard permission at all in Firefox. + if (browserName === 'chromium') + await context.grantPermissions(['clipboard-read', 'clipboard-write']); + else if (browserName === 'webkit') + await context.grantPermissions(['clipboard-read']); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + return page; + })); + + await page1.evaluate(() => navigator.clipboard.writeText('first')); + expect(await page1.evaluate(() => navigator.clipboard.readText())).toBe('first'); + + await page2.evaluate(() => navigator.clipboard.writeText('second')); + expect(await page2.evaluate(() => navigator.clipboard.readText())).toBe('second'); + + expect(await page1.evaluate(() => navigator.clipboard.readText())).toBe('first'); + + await Promise.all([browser1.close(), browser2.close()]); +}); + it('storage access', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31227' } }, async ({ page, context, server, browserName }) => { diff --git a/tests/mcp/clipboard.spec.ts b/tests/mcp/clipboard.spec.ts index 264948b924474..051939050aae4 100644 --- a/tests/mcp/clipboard.spec.ts +++ b/tests/mcp/clipboard.spec.ts @@ -17,9 +17,10 @@ import { test, expect } from './fixtures'; test('clipboard write without permission dialog', async ({ startClient, server, mcpBrowser }) => { - test.skip(mcpBrowser === 'firefox' || mcpBrowser === 'webkit', 'Clipboard permissions are fully supported only in Chromium'); + test.skip(mcpBrowser === 'firefox', 'No such permissions (requires flag) in Firefox'); + const permissions = mcpBrowser === 'webkit' ? 'clipboard-read' : 'clipboard-read,clipboard-write'; const { client } = await startClient({ - args: [`--grant-permissions=clipboard-read,clipboard-write`] + args: [`--grant-permissions=${permissions}`] }); await client.callTool({ name: 'browser_navigate',