Skip to content

feat: add webContents.caretBrowsingEnabled - #52696

Open
snackzone wants to merge 4 commits into
electron:mainfrom
snackzone:zm-caret-browsing-api
Open

feat: add webContents.caretBrowsingEnabled#52696
snackzone wants to merge 4 commits into
electron:mainfrom
snackzone:zm-caret-browsing-api

Conversation

@snackzone

@snackzone snackzone commented Aug 7, 2026

Copy link
Copy Markdown

Description of Change

Adds the ability to enable Chromium's Caret Browsing mode via webContents.caretBrowsingEnabled, setCaretBrowsingEnabled(), and isCaretBrowsingEnabled(). Closes #31339. Note this doesn't add the F7 keyboard shortcut requested in the issue; this is left to be configured by the consuming app.

Screen.Recording.2026-08-10.at.9.55.24.AM.mov

This change primarily exposes the Blink caret_browsing_enabled renderer preference as a settable property on webContents.

Since setting the Blink preference does not update ui::AXPlatform automatically, screen reader integration had to be handled manually here. This was a little tricky since ui::AXPlatform is a process-wide singleton whereas webContents.caretBrowsingEnabled is per-WebContents. Two approaches were considered:

Mirroring

Any WebContents that sets the pref updates ui::AXPlatform. Simple but can lead to states where the pref is out of sync with ui::AXPlatform: disabling caret browsing on Window A while it's enabled on Window B would break the accessibility in Window B - the cursor would still be rendered but screen readers would no longer announce it.

Refcount (what this PR does)

Track the number of open WebContents with caret browsing enabled via a refcount and toggle ui::AXPlatform::GetInstance().SetCaretBrowsingState() accordingly. More book-keeping but guarantees that ui::AXPlatform always has caret browsing enabled for WebContents with the pref enabled. Note that these can still be out of sync - if caret browsing is enabled in any WebContents, then ui::AXPlatform will behave as if it's enabled for all of them. I decided that - where accessibility is concerned - over-reporting is preferable to the under-reporting issue in the mirroring approach.

Notes for reviewers:
  • Although the C++ changes here are small and straightforward, I am not a C++ developer and relied on AI assistance (Opus 5) for those changes.
  • These changes were only tested on MacOS.

Checklist

Release Notes

Notes: Added webContents.caretBrowsingEnabled for toggling caret browsing in a WebContents.

@welcome

welcome Bot commented Aug 7, 2026

Copy link
Copy Markdown

💖 Thanks for opening this pull request! 💖

Semantic PR titles

We use semantic commit messages to streamline the release process. Before your pull request can be merged, you should update your pull request title to start with a semantic prefix.

Examples of commit messages with semantic prefixes:

  • fix: don't overwrite prevent_default if default wasn't prevented
  • feat: add app.isPackaged() method
  • docs: app.isDefaultProtocolClient is now available on Linux

Commit signing

This repo enforces commit signatures for all incoming PRs.
To sign your commits, see GitHub's documentation on Telling Git about your signing key.

PR tips

Things that will help get your PR across the finish line:

  • Follow the JavaScript, C++, and Python coding style.
  • Run npm run lint locally to catch formatting errors earlier.
  • Document any user-facing changes you've made following the documentation styleguide.
  • Include tests when adding/changing behavior.
  • Include screenshots and animated GIFs whenever possible.

We get a lot of pull requests on this repo, so please be patient and we will get back to you as soon as we can.

@electron-cation electron-cation Bot added the new-pr 🌱 PR opened recently label Aug 7, 2026
Exposes the existing Blink renderer preference `caret_browsing_enabled`
as a settable property on webContents. Blink already applies this
preference on every renderer preferences sync, so toggling it takes
effect immediately without a reload.

Enables assistive-technology use cases that need a keyboard-navigable
caret in page content, which Chrome offers but Electron had no API for.

The specs arrow through non-editable prose rather than only reading the
preference back, because Blink honors the caret movement editor commands
in non-editable content only when caret browsing is on — so that is what
actually observes the renderer having received the preference.

Refs electron#31339

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@snackzone
snackzone force-pushed the zm-caret-browsing-api branch 3 times, most recently from 581c824 to 415bc00 Compare August 7, 2026 23:39
Blink draws and moves a caret once caret_browsing_enabled is set, but
assistive technology is never told, so a screen reader stays silent.
Notify ui::AXPlatform too, as Chrome does in
renderer_preferences_util.cc. It is process-wide while the preference is
per-WebContents, so refcount the instances that enable it.

* Caret attributes gate on AXPlatform::IsCaretBrowsingEnabled() before
  falling back to AXNode::HasVisibleCaretOrSelection(), which is false
  for a collapsed caret in non-editable content; untold, macOS reports
  NSNotFound and Windows never fires IA2_EVENT_TEXT_CARET_MOVED.
* A mirror would let one window disabling caret browsing degrade
  accessibility in another; Chrome's input is a uniform profile pref.
* Reconcile from a cached flag, before the preference-equality early
  return and at construction: a <webview> guest inherits the embedder's
  preferences via BrowserPluginGuest::InitInternal(), so a delta taken
  from the preference transition would release an uncounted reference.
* The destructor releases from the cached flag before its early returns,
  since web_contents() may be gone and the state must not stay pinned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@snackzone
snackzone force-pushed the zm-caret-browsing-api branch from 415bc00 to 261fdb3 Compare August 8, 2026 00:10
@snackzone
snackzone marked this pull request as ready for review August 10, 2026 17:56

@erickzhao erickzhao left a comment

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.

API LGTM

Does not close since it doesn't add the F7 keyboard shortcut; this is left to be configured by the consuming app.

IMO that should be good enough to close the issue if we want to leave it to the app developer to implement.

Comment thread docs/api/web-contents.md
Link the "caret browsing" string in both method descriptions to the
property's section, per review feedback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@snackzone

Copy link
Copy Markdown
Author

Does not close since it doesn't add the F7 keyboard shortcut; this is left to be configured by the consuming app.

IMO that should be good enough to close the issue if we want to leave it to the app developer to implement.

SGTM, updated the PR description

@ckerr ckerr left a comment

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.

API LGTM

@ckerr
ckerr self-requested a review August 12, 2026 17:06
@dsanders11 dsanders11 added api-review/requested 🗳 semver/minor backwards-compatible functionality labels Aug 12, 2026
@georgexu99 georgexu99 added api-review/approved ✅ and removed semver/minor backwards-compatible functionality api-review/requested 🗳 labels Aug 12, 2026
@dsanders11 dsanders11 added semver/minor backwards-compatible functionality api-review/requested 🗳 target/44-x-y PR should also be added to the "44-x-y" branch. labels Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-review/requested 🗳 new-pr 🌱 PR opened recently semver/minor backwards-compatible functionality target/44-x-y PR should also be added to the "44-x-y" branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Caret browsing from upstream Chromium

5 participants