feat: add webContents.caretBrowsingEnabled - #52696
Conversation
|
💖 Thanks for opening this pull request! 💖 Semantic PR titlesWe 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:
Commit signingThis repo enforces commit signatures for all incoming PRs. PR tipsThings that will help get your PR across the finish line:
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. |
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>
581c824 to
415bc00
Compare
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>
415bc00 to
261fdb3
Compare
erickzhao
left a comment
There was a problem hiding this comment.
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.
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>
SGTM, updated the PR description |
Description of Change
Adds the ability to enable Chromium's Caret Browsing mode via
webContents.caretBrowsingEnabled,setCaretBrowsingEnabled(), andisCaretBrowsingEnabled(). 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_enabledrenderer preference as a settable property onwebContents.Since setting the Blink preference does not update
ui::AXPlatformautomatically, screen reader integration had to be handled manually here. This was a little tricky sinceui::AXPlatformis a process-wide singleton whereaswebContents.caretBrowsingEnabledis 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 withui::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 thatui::AXPlatformalways 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, thenui::AXPlatformwill 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:
Checklist
npm testpassesRelease Notes
Notes: Added webContents.caretBrowsingEnabled for toggling caret browsing in a WebContents.