Electrolysis/Accessibility/Windows
Overview
The main problem with accessibility on e10s is the need for a11y clients to access and manipulate information about web content -- information that will reside inside one or more sandboxed child processes.
The Windows kernel supports Asynchronous Local Procedure Calls (ALPC). ALPC is a scalable, high-performance IPC mechanism [1] 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."
[1] Russinovich, Mark et al. Windows Internals 6ed, Part 1. Microsoft Press, 2012
Preliminary Design
Introduction
We start by observing that the non-e10s implementation starts with a WM_GETOBJECT message being received by a Firefox window. The message's reply is an IAccessible pointer.
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.
Content Proxies
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 background thread that has been initialized to run inside the multi-threaded apartment (MTA). A corollary to this is that any interaction with the stub (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 the background thread.
To get from the 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, some types may be exceptions to this);
- This protocol shall match its corresponding COM interface as closely as possible.
Inside Gecko, a serialized COM proxy shall 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 the COM proxy to be moved into the chrome process. 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.
A11y Client Considerations
Given the fact that the content process is sandboxed and that e10s is a sea-change in many respects, it 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 with 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 it 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. 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 without requiring DLL injection.