You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Feature] Opt-in to preserve browser-default download behavior when connectOverCDP attaches to a user-owned browser
Context
This issue is a follow-up to #40152, which I filed as a PR and @pavelfeldman rightly closed with: "Most users would want Playwright to automate and see the downloads after connecting to the browser. This deserves an issue first."
Pavel is right on both counts. I skipped the issue step, and the behavior-change default I proposed breaks the historical automation-focused use case. Filing here to discuss the API shape before iterating the code.
The new use case that motivated this
In 2021, connectOverCDP users were automation folks: CI, headless Chrome, Selenoid, remote test runners. For them, Playwright intercepting downloads into artifactsDir is exactly what they want.
Today, connectOverCDP has a second audience: AI browser agents attaching to the human's daily-driver Chrome to observe/act on their real tabs. Examples:
remorses/playwriter — Chrome extension exposing the user's browser as a Playwright session
Selenoid-style "my real browser with my cookies, remote-driven"
For these users, Playwright's Browser.setDownloadBehavior call silently reconfigures the entire browser context — including tabs the human opened manually, hours after the automation session ended. Downloads land in /tmp/playwright-artifacts-XXXXX/<uuid> instead of ~/Downloads, with no filename from Content-Disposition. Chrome's download popup still says "Done". The human has no idea anything is wrong.
How I discovered this
A 15 KB Read.ai transcript disappeared from my ~/Downloads. Chrome said "Done". The file wasn't there. I queried Chrome's SQLite download history DB directly:
SELECT target_path, received_bytes, datetime(start_time/1000000-11644473600,'unixepoch','localtime')
FROM downloads ORDER BY start_time DESCLIMIT5;
Every download from my normal browsing over the past week — including a 1.4 GB video I'd downloaded twice — had been silently rerouted into Playwright's temp folder. 2.6 GB of orphan files in /tmp/playwright-artifacts-GKCPC0/, accumulated because an AI browser agent had attached via CDP at some point and never detached.
The root cause is chromium.ts:_connectOverCDPImpl setting downloadsPath to artifactsDir and acceptDownloads defaulting to 'accept', which then fires Browser.setDownloadBehavior with allowAndName + the temp path — globally, for the whole browser context.
Proposal: opt-in API instead of behavior change
I agree with Pavel's comment on #40152 — changing the default is wrong for the existing automation use case. Instead, I propose a new option:
The persistent context created on connect uses acceptDownloads: 'internal-browser-default' (an existing Playwright value that skips the Browser.setDownloadBehavior CDP call entirely — see crBrowser.ts:353 and launchApp.ts:62)
Chrome handles downloads natively: real filename from Content-Disposition, user's own download folder
Playwright's page.waitForEvent('download') will not fire on the default context, because events were never enabled
Callers who need automation-managed downloads can still create a new context via browser.newContext({ acceptDownloads: true }) — that context gets full Playwright download handling, unchanged
Default behavior (preserveBrowserDownloads omitted or false) is exactly the current behavior. Zero breakage for automation users.
Auto-detect based on whether the browser was launched by Playwright (isLocal flag) — fragile, and users who launch Chrome themselves for local debugging would still get the old behavior.
New option on the context, not the browser — browser.contexts()[0] is already created by the time the caller can touch it, so you can't retroactively change acceptDownloads. Has to be at connect time.
New method entirely — chromium.attachToUserBrowser(url) as a separate API from connectOverCDP. Cleaner semantics but more API surface.
I think option 1 in the proposal (opt-in flag on connectOverCDP) is the minimum change for the maximum compatibility. Happy to discuss whichever shape you prefer.
Happy to re-PR
If this shape works for you, I'll:
Update my fork with the opt-in preserveBrowserDownloads option
Add a test that verifies both paths (default = current behavior, opt-in = new behavior)
Update connect-over-cdp.spec.ts to document both patterns
Link this issue in the new PR
If you'd prefer a different shape, tell me what and I'll build to that.
Discovered via: Anthropic Claude Desktop's built-in chrome-control MCP extension, which uses playwright.chromium.connectOverCDP() under the hood to attach to the user's running Chrome
[Feature] Opt-in to preserve browser-default download behavior when
connectOverCDPattaches to a user-owned browserContext
This issue is a follow-up to #40152, which I filed as a PR and @pavelfeldman rightly closed with: "Most users would want Playwright to automate and see the downloads after connecting to the browser. This deserves an issue first."
Pavel is right on both counts. I skipped the issue step, and the behavior-change default I proposed breaks the historical automation-focused use case. Filing here to discuss the API shape before iterating the code.
The new use case that motivated this
In 2021,
connectOverCDPusers were automation folks: CI, headless Chrome, Selenoid, remote test runners. For them, Playwright intercepting downloads intoartifactsDiris exactly what they want.Today,
connectOverCDPhas a second audience: AI browser agents attaching to the human's daily-driver Chrome to observe/act on their real tabs. Examples:--cdp-endpointremorses/playwriter— Chrome extension exposing the user's browser as a Playwright sessionFor these users, Playwright's
Browser.setDownloadBehaviorcall silently reconfigures the entire browser context — including tabs the human opened manually, hours after the automation session ended. Downloads land in/tmp/playwright-artifacts-XXXXX/<uuid>instead of~/Downloads, with no filename fromContent-Disposition. Chrome's download popup still says "Done". The human has no idea anything is wrong.How I discovered this
A 15 KB Read.ai transcript disappeared from my
~/Downloads. Chrome said "Done". The file wasn't there. I queried Chrome's SQLite download history DB directly:Every download from my normal browsing over the past week — including a 1.4 GB video I'd downloaded twice — had been silently rerouted into Playwright's temp folder. 2.6 GB of orphan files in
/tmp/playwright-artifacts-GKCPC0/, accumulated because an AI browser agent had attached via CDP at some point and never detached.The root cause is
chromium.ts:_connectOverCDPImplsettingdownloadsPathtoartifactsDirandacceptDownloadsdefaulting to'accept', which then firesBrowser.setDownloadBehaviorwithallowAndName+ the temp path — globally, for the whole browser context.Proposal: opt-in API instead of behavior change
I agree with Pavel's comment on #40152 — changing the default is wrong for the existing automation use case. Instead, I propose a new option:
When
preserveBrowserDownloads: true:acceptDownloads: 'internal-browser-default'(an existing Playwright value that skips theBrowser.setDownloadBehaviorCDP call entirely — see crBrowser.ts:353 and launchApp.ts:62)Content-Disposition, user's own download folderpage.waitForEvent('download')will not fire on the default context, because events were never enabledbrowser.newContext({ acceptDownloads: true })— that context gets full Playwright download handling, unchangedDefault behavior (
preserveBrowserDownloadsomitted orfalse) is exactly the current behavior. Zero breakage for automation users.Alternative shapes I considered
isLocalflag) — fragile, and users who launch Chrome themselves for local debugging would still get the old behavior.browser.contexts()[0]is already created by the time the caller can touch it, so you can't retroactively changeacceptDownloads. Has to be at connect time.chromium.attachToUserBrowser(url)as a separate API fromconnectOverCDP. Cleaner semantics but more API surface.I think option 1 in the proposal (opt-in flag on
connectOverCDP) is the minimum change for the maximum compatibility. Happy to discuss whichever shape you prefer.Happy to re-PR
If this shape works for you, I'll:
preserveBrowserDownloadsoptionconnect-over-cdp.spec.tsto document both patternsIf you'd prefer a different shape, tell me what and I'll build to that.
Related
downloadsPath#10700, [Feature] Allow downloads with default context when connectOverCDP is used #12543, [Bug]: Download event's saveAs fails with chromium connected over the CDP #34542, connect_over_cdp always download file to tmp folder playwright-python#2105 — prior closed issues addressing the same underlying problem from the automation-user perspective (all correctly closed at the time because the AI-agent use case didn't exist yet)connectOverCDPdownload path issueEnvironment
main@19e0abb7cchrome-controlMCP extension, which usesplaywright.chromium.connectOverCDP()under the hood to attach to the user's running ChromeFiled by @sidsarasvati.