Accessibility clients require direct access to information about content. In non-e10s this information is queried directly using sync calls into the DOM. These calls generally arrive on the application main or UI thread and expect a response on return. With e10s, the chrome process communicate with sandboxed content through an asynchronous interface, which is incompatible with current accessibility clients. As such the Electrolysis and Accessibility Team are working on new approaches for accessibility clients.
Auto Disable Feature
Currently electrolysis is blocked from running on Windows if recent accessibility use is detected. This blocking may be triggered by 3rd party software that isn't commonly associated with accessibility tools. Tools that can trigger disabling of electrolysis include:
- Vision impaired related assistive clients (screen readers, magnifying glass clients)
- Screen capture utilities (snipping tools)
- Desktop automation utilities (password fillers, window shortcut clients)
- Remote desktop related tools
|Windows||enabled (Fx52)||auto disabled||auto disabled||auto disabled|
|OSX||auto disabled||auto disabled||auto disabled||auto disabled|
|Linux||enabled||enabled||auto disabled||auto disabled|
- browser.tabs.remote.force-enable - Force electrolysis on regardless of accessibility use.
- accessibility.force_disabled - Force disable accessibility use, useful if you run into stability issues running electrolysis. Note: if you change this pref you also need to reset the two time-related ones below in order for e10s to reactivate.
When electrolysis is blocked by accessibility the reason will be reported to the Browser Console at startup. Two user preferences will also be set indicating accessibility recently loaded:
- accessibility.lastLoadDate - time of the last load. Electrolysis will be disabled for seven days post this time.
- accessibility.loadedInLastSession - boolean indicating a recent load. Electrolysis will always be disabled if the last session used accessibility features.
Embedded platform code that handles e10s blocking.
OSX and Linux
Mac and Linux currently leverage a sync IPDL chrome to sandboxed content process api that connect accessibility clients to content. Access to chrome is unchanged. Over time chrome side caching of content DOM will be added to cut down on sync communications, which can cause deadlocks.
The Windows kernel supports Asynchronous Local Procedure Calls (ALPC). ALPC is a scalable, high-performance IPC mechanism  that, while undocumented, is available to application software via Microsoft RPC (when using the "ncalrpc" transport). Since COM marshaling is built atop RPC, it is possible for properly configured COM proxies to take advantage of the performance that ALPC offers. Since MSAA is built atop COM, Gecko could configure its COM proxies to gain the benefits of ALPC "for free."
If a11y data must first be sent from chrome to content, it must be serialized by IPDL. It would then be serialized a second time, in a different format, by COM. Direct communication between the a11y client and content allows us to eliminate the IPDL serialization.
 Russinovich, Mark et al. Windows Internals 6ed, Part 1. Microsoft Press, 2012
A11y Client Considerations
Given the fact that the content process is sandboxed and that e10s is a sea-change in many respects, now is a good time to review the interactions between a11y clients and Gecko. We will not be allowing DLL injection into the content process, and would prefer that this be avoided in the case of the chrome process as well. Instead, we would prefer that a11y clients stick to the COM-based MSAA interface outside of the Firefox chrome process. One major motivation for making our interface proxies work over ALPC is that ALPC was designed to support IPC for user-mode drivers. If ALPC is up to the task of supporting device drivers, we believe it will be more than sufficient for a11y IPC.
Having said that, we are aware that many clients are injecting code into Firefox to process a11y events as quickly as possible; obviously ALPC will still be slower than a virtual function call to some degree. We would like to hear more from vendors about their use cases that require this injection. Perhaps we can develop additional COM interfaces to help satisfy the clients' needs and mitigate the necessity for DLL injection.
(Dashed arrows represent COM atop RPC, using the ncalrpc transport.)
This diagram demonstrates the state of a11y IPC connections after a content proxy has been returned to the client.
When the WM_GETOBJECT sender is out-of-process, this IAccessible is implicitly proxied by Microsoft COM. The a11y client is not directly invoking the object's methods. Instead, the methods are being invoked via COM marshaling. Marshaling and IPC occur automatically and transparently to the caller.
This mechanism will also work in the e10s case, but with the IAccessible interface being provided by content processes. Clever management of the proxy objects by the chrome process will make this entire scheme completely transparent to the a11y client: the client does not need to be aware of which process it is communicating with.
Unlike the chrome case, COM proxies for content processes must be explicitly created. Microsoft COM provides APIs to create proxies and (de)serialize them, however this must be done with care. In order to take advantage of ALPC, the proxy for a content IAccessible must be created on a thread that has been initialized to run inside the multi-threaded apartment (MTA). Since our main thread runs in the single-threaded apartment (STA), any interaction with the "stub" (which is the server side of the proxy) must also occur on a MTA thread. Instead of calling straight into a11y APIs on the main thread, proxied calls will arrive on a background thread that is initialized for the MTA.
To move data from a background thread to the main thread, we propose using a simplified IPDL protocol:
- We should avoid IPDL deferred message protection for stability and performance reasons;
- This protocol shall not serialize complex types. Since it is merely doing an in-process hand-off of data between threads, it should just pass pointers whenever possible (note that due to COM restrictions, any parameters that are COM interfaces will be exceptions to this);
- This protocol shall match its corresponding COM interface as closely as possible.
Inside Gecko, a serialized COM proxy will be encapsulated within a ProxyStream object. The ProxyStream class will be a first-class type in IPDL. The PBrowser protocol will be augmented with APIs to asynchronously transmit ProxyStream objects between chrome and content. This will allow serialized COM proxies to be moved between chrome and content processes. Once inside the chrome process, ProxyStream will deserialize the proxy and return an interface pointer that may be used in replies to WM_GETOBJECT requests. Since handling WM_GETOBJECT is synchronous, we want the proxied interfaces to be present in the chrome process ahead of time.
So far we have identified the following cases where we will need to resolve proxy objects:
- When DOM traversal crosses the threshold between chrome and content in either direction;
- When WM_GETOBJECT specifies an object identifier that resolves to an object that resides in content. The chrome process will need to maintain a mapping from object ids to proxy interfaces.
We need to be careful with the possibility of reentry when firing a11y events via NotifyWinEvent. Provided that all interfacing with a11y clients is out-of-process, this might not be as serious a concern as originally thought, since NotifyWinEvent will return asynchronously and any incoming calls over COM interfaces will be proxied to the background MTA thread. We may encounter scenarios that we haven't yet thought of, however.