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 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. Our nsIAccessible implementation can proxy further to a variety of classes, each specialized for a particular kind of widget or data node.
The first thing that happens when an accessibility aid wants to watch our application is it sends the main application window a WM_GETOBJECT message requesting an IAccessible for the window. 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 IAccessible to get at the rest of the tree, 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 a RootAccessible IAccessible for the window we get a 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. Next the new RootAccessible is created. RootAccessible and Accessible are both implemented in mozilla/widget/src/windows/Accessible.cpp).
The impementation for nsIAccessible 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 QueryInterfacing the DOM node's primary frame for an nsIAccessible. If it gets one, it's considered an accessible object. A frame that wishes to return an nsIAccessible when QI'd for it, creates one of the correct type on the fly using nsIAccessibilityService.
The specific implementations of nsIAccessible for each widget or content type inherit from nsGenericAccessible, which simply returns NS_ERROR_NOT_IMPLEMENTED for every method. The specific 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. It doesn't need to do this, however, for images, because an implementation exists for nsHTMLImageAccessible::getAccFirstChild (returns the first image map area for the image if one exists).
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 a WIN32 call to register as a listener for these events.
The accessibility aid is choose which events it is interested in learning more about by sending a window a WM_???? event requesting the IAccessible to the node corresponding to the child number that had been indicated from NotifyWinEvent. 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.