Closed Bug 2062852 Opened 20 days ago Closed 10 days ago

Make accessibility.force_disabled=-1 less platform specific

Categories

(Core :: Disability Access APIs, enhancement)

enhancement

Tracking

()

RESOLVED FIXED
156 Branch
Tracking Status
firefox156 --- fixed

People

(Reporter: Jamie, Assigned: Jamie)

References

Details

Attachments

(4 files)

accessibility.force_disabled=-1 is used to force enable accessibility for testing purposes. Currently, it is implemented differently on every platform (and not at all on some platforms). Even though the pref is read centrally in nsAccessibilityService, that code won't be triggered unless GetOrCreateAccService is called from platform specific code. This is very confusing, makes it hard to trust the pref across platforms and makes the code hard to reason about. It wouldn't surprise me if different platforms end up creating the service at different times. Also, I'm fairly sure it just isn't implemented on Android at all.

Assignee: nobody → jteh
Status: NEW → ASSIGNED

On some platforms you can have a11y enabled on a single app and examine it via ATs or other tools. I don't think that is the case in Android, or at least it wasn't in the past. If you try to dispatch an a11y event to the platform it will throw an exception. So the use of forcing a11y on in Android is limited to the in-process piece. That is also how the junit tests work. This means that force_disabled=-1 would mean something different there.

(In reply to Eitan Isaacson [:eeejay] from comment #2)

I don't think that is the case in Android, or at least it wasn't in the past. If you try to dispatch an a11y event to the platform it will throw an exception.

Thanks for the info. I see we already catch that exception to deal with XPCOM instantiation in mochitests. So I don't think there's anything new we'd need to do to handle this.

So the use of forcing a11y on in Android is limited to the in-process piece. ... This means that force_disabled=-1 would mean something different there.

That's true and it's probably worth noting that somewhere. I still think this is useful to have though, particularly when you're trying to reproduce a crash or something locally in a non-a11y test. It seems CI sometimes instantiates accessibility at the OS level, but that seems to be much harder to do on a local machine.

See Also: → 538530, 761589, 1342245

accessibility.force_disabled=-1 is meant to force enable accessibility, but previously, nothing acted on it centrally.
GetOrCreateAccService only checked the force disabled case, leaving the force enabled case to platform specific code.
Aside from being hard to reason about, this meant there was different (sometimes broken) behaviour on different platforms.
Windows was the only platform that proactively started accessibility in nsWindow::Create.
On Mac and Linux, the force enabled state allowed accessibility to be enabled where it otherwise wouldn't have been (e.g. no detected client), but it still only activated based on specific platform signals.
Android never read the pref at all.

To fix this, add a11y::MaybeStartForceEnabled, called once from nsAppRunner.cpp just before the main run loop starts.
This only ever runs in the parent process.
Calling PlatformDisabledState() there unconditionally also ensures the pref-change callback gets registered even when nothing else would have triggered that registration.

This required an additional fix on Android.
Previously, SessionAccessibility::Settings unconditionally shut accessibility down when a session's accessibility was first attached to a view with no assistive technology running.
We now prevent that shutdown when force enabled via the pref.

This is now redundant.
a11y::MaybeStartForceEnabled, called centrally from nsAppRunner.cpp, covers this on every platform including Windows.

PrefChanged already force shuts accessibility down when the pref changes to 1.
However, it previously did nothing on a change to -1, so force enabling required a restart.
Give MaybeStartForceEnabled an async flag and call it from PrefChanged with that mode when the pref changes to force enabled.
Async is needed because this callback runs synchronously wherever the pref is set.
We don't want accessibility init happening inside arbitrary code.
Also update the comment describing the pref.

See Also: → 2035599

Previously, we didn't unregister from ATK when shutting down.
That was fine because we never supported starting the engine up again after it was shut down, but we soon will.
At that point, failure to unregister from ATK will cause subsequent registration attempts to do nothing, so clients can't find us.
To fix this, call atk_bridge_adaptor_cleanup when shutting down.

Attachment #9626554 - Attachment description: Bug 2062852 part 3: Start accessibility when accessibility.force_disabled changes to -1 at runtime. r?eeejay! → Bug 2062852 part 4: Start accessibility when accessibility.force_disabled changes to -1 at runtime. r?eeejay!
Pushed by jteh@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/2e9006c7240b https://hg.mozilla.org/integration/autoland/rev/c5e8ba0fdb08 part 1: Centrally handle accessibility.force_disabled=-1 (force enable) for all platforms. r=eeejay https://github.com/mozilla-firefox/firefox/commit/3595a736aa06 https://hg.mozilla.org/integration/autoland/rev/685b5dcd757d part 2: Remove the Windows specific accessibility force enable priming hack. r=eeejay https://github.com/mozilla-firefox/firefox/commit/c8ef71edc46b https://hg.mozilla.org/integration/autoland/rev/1f0db270a191 part 3: Enable ATK to be reinitialised after it is shut down. r=eeejay https://github.com/mozilla-firefox/firefox/commit/9752743055a8 https://hg.mozilla.org/integration/autoland/rev/8d1e6e307f8b part 4: Start accessibility when accessibility.force_disabled changes to -1 at runtime. r=eeejay,geckoview-reviewers,m_kato
Pushed by rperta@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/c1e376bfc07a https://hg.mozilla.org/integration/autoland/rev/b64bd3f67d32 Revert "Bug 2062852 part 4: Start accessibility when accessibility.force_disabled changes to -1 at runtime. r=eeejay,geckoview-reviewers,m_kato" for causing ThreadSanitizer failures

Backed out for causing ThreadSanitizer failures
Backout link
Push with failures
Failure log(s)

Flags: needinfo?(jteh)

I confess I don't have much of an understanding of the DBus and ATK code, but this appears to be a bug in DBus. Analysis below (heavily Claude assisted):

The failure was a TSAN lock-order-inversion abort.

TSAN detected a 3-mutex lock-order-inversion cycle (M0 -> M1 -> M2 -> M0) across two threads:

  1. Audio IPC thread (T97), inside audio_thread_priority::rt_linux::get_limits:
    holds M0, acquires M1 inside dbus_bus_register (talking to rtkit over the system bus for realtime scheduling).
  2. Main thread, inside nsAccessibilityService::FullInit -> PlatformInit -> atk_bridge_adaptor_init:
    holds M1, acquires M2 inside dbus_connection_send_with_reply_and_block (AT-SPI registration).
  3. Main thread, inside nsAccessibilityService::Shutdown -> PlatformShutdown -> atk_bridge_adaptor_cleanup:
    holds M2, acquires M0 while closing the AT-SPI bus connection (dbus_connection_close).

Root cause, traced into libdbus and at-spi2-atk source:

  • M0 and M1 are file-scope global mutexes in dbus/dbus-bus.c ("bus" and "bus_datas"), shared by every DBusConnection obtained via dbus_bus_get()/dbus_bus_register(), regardless of bus type or shared vs private connections. internal_bus_get() nests them as bus -> bus_datas -> connection-lock.
  • notify_disconnected_unlocked() in dbus-connection.c runs for any connection disconnect and nests the connection's own lock -> bus, the reverse order. This is a genuine inversion inside libdbus itself, not specific to our usage.
  • atk_bridge_adaptor_cleanup() (at-spi2-atk bridge.c) calls dbus_connection_close() on the AT-SPI bus connection, triggering the disconnect path above.

Options considered and rejected:

  1. Use a private D-Bus connection for atk-bridge or audio_thread_priority: does not help. internal_bus_get() takes the "bus" lock unconditionally regardless of the private flag, and any connection close goes through the same disconnect path.
  2. Patch audio_thread_priority (vendored, Mozilla-authored crate) to avoid dbus_bus_get()/dbus_bus_register() for its rtkit call: technically avoids that thread's contribution to the cycle, but only patches one side and leaves the actual libdbus lock-order defect and our own repeated connection churn in place.
  3. File upstream with libdbus: likely a legitimate report (two of its own functions nest the same two global locks in opposite order), but not something we can rely on for this bug's timeline.
  4. Get atk-bridge to expose a lighter "reannounce the app without closing the connection" API: the underlying protocol already separates connection setup from app registration (register_application/deregister_application send plain messages over an already-open connection), but this is not exposed publicly. Only atk_bridge_adaptor_init()/atk_bridge_adaptor_cleanup() are the public entry points, and atk-bridge is a system library loaded via dlopen, not something Firefox can patch or extend.
  5. TSAN suppression: bug 1824768 already suppresses unrelated libdbus mutex noise from external-library startup issues, a different suppression category to this lock-order-inversion. However, this papers over a real hazard rather than fixing it.
Flags: needinfo?(jteh)
Pushed by jteh@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/db7b3eedf872 https://hg.mozilla.org/integration/autoland/rev/24746728bb6b part 1: Centrally handle accessibility.force_disabled=-1 (force enable) for all platforms. r=eeejay https://github.com/mozilla-firefox/firefox/commit/e76da9eb3cfc https://hg.mozilla.org/integration/autoland/rev/5dbd841c7767 part 2: Remove the Windows specific accessibility force enable priming hack. r=eeejay https://github.com/mozilla-firefox/firefox/commit/9bbdbfb27a87 https://hg.mozilla.org/integration/autoland/rev/c0e18acfdcbe part 3: Enable ATK to be reinitialised after it is shut down. r=eeejay https://github.com/mozilla-firefox/firefox/commit/7c14fb003c14 https://hg.mozilla.org/integration/autoland/rev/e59e6960ee85 part 4: Start accessibility when accessibility.force_disabled changes to -1 at runtime. r=eeejay,geckoview-reviewers,m_kato
Regressions: 2065725
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: