Skip to content

feat(cdp): honor Browser.grantPermissions / setPermission / resetPermissions#2727

Merged
karlseguin merged 3 commits into
lightpanda-io:mainfrom
rohitsux:feat/cdp-browser-permissions
Jun 18, 2026
Merged

feat(cdp): honor Browser.grantPermissions / setPermission / resetPermissions#2727
karlseguin merged 3 commits into
lightpanda-io:mainfrom
rohitsux:feat/cdp-browser-permissions

Conversation

@rohitsux

Copy link
Copy Markdown
Contributor

Browser.grantPermissions, Browser.setPermission and Browser.resetPermissions were noops, so a CDP automation client (Puppeteer's browserContext.grantPermissions(['geolocation']), Playwright's context.grantPermissions) could not influence what navigator.permissions.query() returns. The query always reported "prompt" regardless. This is a gap for automation flows that need to pre-grant or deny permissions (geolocation, notifications, clipboard, etc.) before a page runs.

Changes

  • src/browser/Page.zig: store permission state on the Page, keyed by permission name, with a small setPermission helper. Keys and values are duped into the Page arena, so the state lives for (and resets with) the Page.
  • src/cdp/domains/browser.zig: implement the three methods. grantPermissions sets each listed permission to "granted"; setPermission sets a single permission to an explicit "granted"/"denied"/"prompt" state; resetPermissions clears the map.
  • src/browser/webapi/Permissions.zig: navigator.permissions.query() now reads the stored state and falls back to "prompt" when unset (previously hardcoded "prompt").

Scope note

Permission state is stored on the active Page, so it resets on navigation. CDP scopes these methods to a browser context (state persists across navigations); persisting across navigations would mean storing on the CDP BrowserContext and re-applying on each new Page, which I left as a follow-up to keep this change small. The origin / browserContextId parameters are accepted and ignored for now.

Verified

  • make test passes (796/796, no leaks); new test cdp.browser: grant/set/reset permissions reach navigator.permissions drives all three methods through processMessage and asserts the stored state.
  • zig fmt --check ./*.zig ./**/*.zig clean.
  • make build succeeds.

Comment thread src/cdp/domains/browser.zig Outdated
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
for (params.permissions) |name| {
try page.setPermission(name, .granted);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we set permissions at the browser's level?
Here, any permission set via CDP will be lost with the page.

Also regarding the origin support, if we don't support it right now, we should at least write down a not_implemented log.
Same about browserContextId.

@rohitsux

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed all three points:

  • Moved the permission store from the active `Page` to the `Browser` (mirroring how `http_client` holds the user-agent override), so permissions now persist across page navigations instead of being lost with the page. `navigator.permissions.query()` reads the browser-level state via `frame._session.browser`.
  • Added `not_implemented` log warnings for `origin` and `browserContextId` on both `grantPermissions` and `setPermission`, since they are accepted but not yet honored.

The test now asserts the state on the browser. `make test` passes (796/796, no leaks), `zig fmt --check` is clean, and `make build` succeeds.

@rohitsux rohitsux force-pushed the feat/cdp-browser-permissions branch from 5f23b86 to da08031 Compare June 16, 2026 19:48
@rohitsux

Copy link
Copy Markdown
Contributor Author

Rebased the browser-level change on top of your PermissionState enum refactor (it had gotten clobbered in an earlier merge on my end). The browser store is now StringHashMapUnmanaged(PermissionState), setPermission takes the enum, and Browser.setPermission from CDP parses the setting via std.meta.stringToEnum. Force-pushed. make test is green (796/796, no leaks).

rohitsux and others added 3 commits June 19, 2026 07:28
…issions

These three Browser methods were noops, so a CDP client could not change
what navigator.permissions.query() reports - it always returned "prompt".

Store permission state on the active Page (keyed by permission name) and
have navigator.permissions.query() read it back. grantPermissions sets each
listed permission to "granted", setPermission sets a single permission to an
explicit state, and resetPermissions clears them (query falls back to
"prompt"). State is scoped to the Page and resets on navigation.

Adds a CDP round-trip test exercising all three methods.
Address review: permissions set via Browser.grantPermissions / setPermission
now live on the Browser instead of the active Page, so they persist across
page navigations (previously a navigation dropped them) and match how Chrome
scopes permissions to the browser context. navigator.permissions.query() reads
the browser-level state. Also log not_implemented for the origin and
browserContextId params, which are accepted but not yet honored.
@karlseguin karlseguin force-pushed the feat/cdp-browser-permissions branch from da08031 to a0fc38e Compare June 18, 2026 23:30
@karlseguin karlseguin merged commit 6b42cad into lightpanda-io:main Jun 18, 2026
23 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants