See also: Gecko Info for Windows Accessibility Vendors, a primer for vendors of 3rd party accessibility software, on how to use our MSAA and other relevant API's.
The Accessible module is where we implement support for the Microsoft Active Accessibility (MSAA) API (bug 12952). Support for Sun's Gnome Accessibility API is part of our future plans as well.
A 3rd part accessibility aid, such as a screen reader, wants to track what's happening inside Mozilla. It needs to know about focus changes and other events, and it needs to know whtat data nodes there are in the layout tree. Using this information, the screen reader will speak out loud important changes to the document or UI, and allow the user to track where they navigate. Some screen readers also magnify text and images in the currently focused area, and others show information on a refreshable braille display.
In Windows, accesibility aids acquires the necessary information to do this using hacks and MSAA. MSAA is supposed to be the "right way" for accessibility aids to get information, but sometimes the hacks are more effective. For example, screen readers look for screen draws of a vertical blinking line, to determine the location of the caret. Without doing this, screen readers would not be able to let the user know where there caret has moved to in most programs, because so many applications do not use the system caret (ours is an example). This is so commonly done, that no one even bothers to support the MSAA caret, because the hack works.
MSAA provides information in two different ways:
To really learn about MSAA, you need to download the entire MSAA SDK. Without downloading the SDK, you won't get the complete documentation. The SDK also contains some very useful tools, such as the Accessible Event Watcher, which shows what accessible events are being generated by a given piece of software. The Accessible Explorer and Inspect Object tools show the tree of data nodes the Accessible object is exposing through COM.
The IAccessible interface is used in a tree of IAccessible's, each one representing a data node, similar to a DOM.
Here are the methods supported in IAccessible:
Rather than directly implement IAccessible with an Accessible class, we have chosen to proxy to our own cross-platform interface, called nsIAccessible, which is more robust. It has the capability of supporting other new accessibility API's such as Sun's Gnome Accessiblity API. The nsIAccessible interface is implemented by a variety of classes for each of the various objects in HTML. Each class is tailored to the specific abilities and properties of the HTML objects it applies to.
The first thing that happens when an accessibility aid wants to watch our application is calls the Windows API function AccessibleObjectFromWindow(). This in turns sends the window in question a WM_GETOBJECT message requesting an IAccessible for the window. In our case, this event is received in mozilla/widget/src/windows/nsWindow.cpp. We send back an IAccessible interface that represents that root window. The accessibility aid will use that first IAccessible to reach rest of the IAccessible hierarchy, by asking for it's children IAccessibles, asking the children for the grandchildren IAccessibles, and so on. Until this WM_GETOBJECT message is processed, the accessibility.dll is not loaded, so there is almost zero overhead for accessibility in Mozilla.
To create the root IAccessible for a window the first time it gets the WM_GETOBJECT message in, nsWindow.cpp first generates an internal event called NS_GETACCESSIBLE, which is handled in nsFrame.cpp via the creation of an nsRootAccessible implementation of the nsIAccessible interface. The first IAccessible is then created by instantiating a RootAccessible class. This RootAccessible is also cached by the nsWindow it's for, so that any additional WM_GETOBJECT messages use the same RootAccessible. The RootAccessible class used to implement IAccessible here is slightly different from the normal Accessible class that's used, in that it keeps track of event data. RootAccessible and Accessible are both implemented in mozilla/widget/src/windows/Accessible.cpp).
The implementations of IAccessible (Accessible and RootAccessible), don't know anything about Mozilla objects. They merely proxy to our cross platform accessibility classes, which all have an nsIAccessible interface.
The base implementation for nsIAccessible is called nsAccessible. It has default implementations for all the nsIAccessible methods. It also knows how to walk Mozilla's content DOM and frame tree, exposing only the objects that are needed for accessibility. Essentially, nsAccessible knows what it needs to expose by asking each DOM node's primary frame for an nsIAccessible, using the GetAccessible() method. If it gets one, it's considered an accessible object. A frame that wishes to return an nsIAccessible GetAccessible() is called, creates one of the correct type on the fly using nsIAccessibilityService methods built for that purpose.
The specific implementations of nsIAccessible for each widget or content type inherit from nsAccessible. Each implementation then overrides those methods it wishes to implement, and does nothing for those methods it wants the default behavior for. For example, the default behavior for nsIAccessible::getAccFirstChild is to instantial a nsDOMTreeWalker, and ask it for the first child. However, nsImageAccessible overrides getAccFirstChild, returning the first area of an image map if there is one, otherwise nsnull.
When an accessibility-related event occurs within an application such as Mozilla, it must use NotifyWinEvent from the Win32 API. NotifyWinEvent is passed arguments for the window the event occured in, and the number of the child within that window. Accessibility aids use the Win32 call SetWinEventHook() to register as a listener for these events.
The accessibility aid is choose which events it is interested in learning more about by using the Win32 API call AccessibleObjectFromEvent, requesting the IAccessible to the node corresponding to the child number that had been indicated from NotifyWinEvent(). This ends up asking our RootAccessible for the child IAccessible that matches the window handle and child id we indicated through NotofyWinEvent().
In Mozilla, this creates a problem. We cannot keep track of a child number for every important accessible node in a document. We deal with this by generating fake child IDs for the most recent accessibile events that we have generated, in a circular array.
Since there is a RootAccessible for each top level window that might generate MSAA events, that's where we do the bookkeeping for these events and their nsIAccessible's. Whenever NotifyWinEvent() is called, a new fake ID is generated (We use negative numbers for the fake IDs). When the callback comes to request the IAccessible for that child number, we check the circular array for that ID, and voila, we have the corresponding nsIAccessible to proxy.
Every RootAccessible has an nsRootAccessible which is an nsIAccessibleEventReceiver. The RootAccessible uses this to register itself as an nsIAccessibleEventListener. In the end, nsRootAccessible registers itself as a listener of Mozilla's internal and DOM events. It's HandleEvent routine translates these events into MSAA events, and passes them along to with an nsIAccessible to the original RootAccessible::HandleEvent which turns that nsIAccessible into a NotifyWinEvent call, complete with a fake child ID.
Most MSAA events aren't utilized by accessibility aids. Therefore we implement only the handful that matter. The most important event is the focus event, followed by name, state and value change events.