Skip to content

Special attribute for macOS accessibility#10305

Merged
zcbenz merged 5 commits into
electron:masterfrom
ivmirx:macos-accessibility
Sep 11, 2017
Merged

Special attribute for macOS accessibility#10305
zcbenz merged 5 commits into
electron:masterfrom
ivmirx:macos-accessibility

Conversation

@ivmirx

@ivmirx ivmirx commented Aug 19, 2017

Copy link
Copy Markdown
Contributor

In the issue #7206 we were discussing that Electron apps are inaccessible unless VoiceOver is enabled. While it's a working solution for users with vision impairment, all other users and apps that require accessibility can't interact with Electron-based software because they don't keep VoiceOver running.

I suggest adding AXManualAccessibility for programmatically enabling it in Electron apps. The reason for a new attribute is that AXEnhancedUserInterface is already reserved by VoiceOver.

Adding this attribute will allow both Electron developers and 3rd party developers to enable and disable accessibility from their code by calling accessibilitySetValue:forAttribute: on the application.

It will be also possible to create a small utility app to switch accessibility in Electron-based apps until there's a native UI solution (like the accessibility settings page in Chrome).

In the linked issue we were discussing that Electron apps are inaccessible unless VoiceOver is enabled. While it's a working solution for users with vision impairment, all other users and apps that require accessibility can't interact with Electron-based software because they don't keep VoiceOver running.

I suggest adding `AXManualAccessibility` for programmatically enabling it in Electron apps. The reason for a new attribute is that `AXEnhancedUserInterface` is already reserved by VoiceOver.

Adding this attribute will allow both Electron developers and 3rd party developers to enable and disable accessibility from their code by calling `accessibilitySetValue:forAttribute:` on the application.

It will be also possible to create a small utility app to switch accessibility in Electron-based apps until there's a native UI solution (like the accessibility settings page in Chrome).
@welcome

welcome Bot commented Aug 19, 2017

Copy link
Copy Markdown

💖 Thanks for opening this pull request! 💖

Here is a list of things that will help get it 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.

@ivmirx ivmirx changed the title Special attribute for macOS accessibility #7206 Special attribute for macOS accessibility Aug 19, 2017
@zeke

zeke commented Aug 21, 2017

Copy link
Copy Markdown
Contributor

Thanks, @ivmirx. Can you add some API documentation for this?

@ivmirx

ivmirx commented Aug 22, 2017

Copy link
Copy Markdown
Contributor Author

@zeke thank you for looking into this. I added the macOS case to the accessibility tutorial.

The example code will work from Cocoa apps and should also work via NodObjC for Electron apps. But maybe I'm missing an easier way for Electron apps to switch their own accessibility.

@zeke

zeke commented Aug 22, 2017

Copy link
Copy Markdown
Contributor

maybe I'm missing an easier way for Electron apps to switch their own accessibility

Yeah this would be ideal. End-users of Electron should generally not be exposed to C++ code, but should rather use a JavaScript API that hooks into the underlying C++ implementation.

@ivmirx

ivmirx commented Aug 24, 2017

Copy link
Copy Markdown
Contributor Author

I added an accessibility setter to the app bindings and updated docs with a warning about performance.

Overall I haven't experienced noticeable performance issues when running many Chrome tabs with accessibility enabled (Mac Mini 2012) but there's some overhead according to dev tools (depending on DOM complexity and update frequency).

Comment thread docs/api/app.md Outdated
https://www.chromium.org/developers/design-documents/accessibility for more
details.

### `app.setAccessibilitySupportEnabled(value)` _macOS_ _Windows_

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would suggest using a more descriptive name than value for the boolean. Something like enabled would be more consistent with other APIs that accept boolean arguments:

  • win.setResizable(resizable)
  • win.setMovable(movable)
  • win.setMinimizable(minimizable)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, it was enabled initially but then I decided to follow the app.commandLine.appendArgument(value) nearby. Fixed!

@zeke zeke left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

From an user perspective, this is looking great. Left a few comments on documentation and expected behavior.

Comment thread docs/api/app.md Outdated

### `app.setAccessibilitySupportEnabled(enabled)` _macOS_ _Windows_

* `enabled` Boolean - Enable or disable accessibility tree rendering

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is accessibility support disabled by default? If so, these docs should probably mention that.

For folks who don't know what the "accessibility tree rendering" is, a link would be helpful. Is this the right URL? https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's disabled by default both in Chrome and Electron. The link is super helpful, adding it.


### Assistive Technology

Electron application will enable accessibility automatically when it detects assistive technology (Windows) or VoiceOver (macOS). See Chrome's [accessibility documentation](https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology) for more details.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"Electron application will enable accessibility automatically" -- just so I understand correctly: does this mean the presence of these assistive features will automatically cause accessibility to be enabled, regardless of the current setting?

Or does it only mean that these features will be used if setAccessibilitySupportEnabled(true) was explicitly called in the app?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The first one – system assistive utilities have a priority and will enable accessibility regardless of the setAccessibilitySupportEnabled(). So an Electron app's developer can be unaware of this method and accessibility will still work for many users who have VoiceOver on.

For users who need accessibility without VoiceOver there are 3 options:

  • ask an Electron app's developer to expose accessibility switch in settings using setAccessibilitySupportEnabled(true)
  • ask their assistive software developer to start applying the AXManualAccessibility attribute on Electron apps
  • use some custom utility to apply AXManualAccessibility on Electron apps (I'm going to create an open-source one later)

Comment thread docs/tutorial/accessibility.md Outdated

Electron application will enable accessibility automatically when it detects assistive technology (Windows) or VoiceOver (macOS). See Chrome's [accessibility documentation](https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology) for more details.

On macOS 3rd party assistive technology can switch accessibility inside Electron applications by setting the attribute `AXManualAccessibility` programmatically:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On macOS, third-party

@zcbenz zcbenz merged commit e6733b4 into electron:master Sep 11, 2017
@welcome

welcome Bot commented Sep 11, 2017

Copy link
Copy Markdown

Congrats on merging your first pull request! 🎉🎉🎉

divanshu-go added a commit to divanshu-go/screenpipe that referenced this pull request Jun 9, 2026
Quitting the recorder (e.g. from the tray) could inject ghost keystrokes
into the focused app — typed text coming back mangled or with garbage
appended (e.g. "hello hello" → "ello ell ell"), most visibly in
Chromium/Electron apps like Discord.

Root causes fixed:

- Probe tap on every permission poll: removed probe_listen_event_tap().
  Permission checks now read INPUT_MONITORING_GROUND_TRUTH (populated by
  the real capture tap) falling back to CGPreflightListenEventAccess.
  Modelled after keycastr/keycastr@1025e8f

- Shutdown race: tap callback now checks state.stop (Acquire) first and
  drops events immediately when teardown begins.

- InstalledTap RAII guard: idempotent teardown — set_enabled(false) →
  remove_src → invalidate src → invalidate tap → drain run loop.
  Drop impl as safety net against panics.

- TAP_DISABLED_BY_TIMEOUT replay burst: re-enable tap immediately via
  tap_ptr; use try_lock in callback so a slow flush can't stall past the
  kernel timeout.

- Chromium AX replay: remove AXObserver notifications before teardown,
  clear AXManualAccessibility to signal graceful disconnect, drain run
  loop 3× so Chromium flushes buffered keystrokes instead of replaying.
  Ref: electron/electron#10305

- Memory ordering: Relaxed → Acquire on all stop-flag reads.

- TapState Box::leak reclaimed on stop (was a per-session leak).

Regression tests: test_no_ghost_keystrokes_after_stop,
test_stop_flag_concurrent. UI input-monitoring card updated to reflect
the no-probe model.

Closes screenpipe#3789
louis030195 pushed a commit to screenpipe/screenpipe that referenced this pull request Jun 9, 2026
…raceful shutdown (#3916)

* fix(a11y): eliminate ghost keystrokes on macOS recorder shutdown

Quitting the recorder (e.g. from the tray) could inject ghost keystrokes
into the focused app — typed text coming back mangled or with garbage
appended (e.g. "hello hello" → "ello ell ell"), most visibly in
Chromium/Electron apps like Discord.

Root causes fixed:

- Probe tap on every permission poll: removed probe_listen_event_tap().
  Permission checks now read INPUT_MONITORING_GROUND_TRUTH (populated by
  the real capture tap) falling back to CGPreflightListenEventAccess.
  Modelled after keycastr/keycastr@1025e8f

- Shutdown race: tap callback now checks state.stop (Acquire) first and
  drops events immediately when teardown begins.

- InstalledTap RAII guard: idempotent teardown — set_enabled(false) →
  remove_src → invalidate src → invalidate tap → drain run loop.
  Drop impl as safety net against panics.

- TAP_DISABLED_BY_TIMEOUT replay burst: re-enable tap immediately via
  tap_ptr; use try_lock in callback so a slow flush can't stall past the
  kernel timeout.

- Chromium AX replay: remove AXObserver notifications before teardown,
  clear AXManualAccessibility to signal graceful disconnect, drain run
  loop 3× so Chromium flushes buffered keystrokes instead of replaying.
  Ref: electron/electron#10305

- Memory ordering: Relaxed → Acquire on all stop-flag reads.

- TapState Box::leak reclaimed on stop (was a per-session leak).

Regression tests: test_no_ghost_keystrokes_after_stop,
test_stop_flag_concurrent. UI input-monitoring card updated to reflect
the no-probe model.

Closes #3789

* style(a11y): apply rustfmt to shutdown changes

Fixes the cargo fmt --all --check failure (the only red required check).
Pure formatting, no logic change.

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

---------

Co-authored-by: Louis Beaumont <louis@screenpi.pe>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants