mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-14 15:37:55 +00:00
94198ed5d8
-r aarol, jgaunt -sr brendan
165 lines
10 KiB
HTML
165 lines
10 KiB
HTML
<html>
|
|
<head>
|
|
<title>
|
|
How the Accessible Module works
|
|
</title>
|
|
</head>
|
|
<body>
|
|
<h1>How the Accessible module (accessibility.dll) works</h1>
|
|
<ul>
|
|
<p>
|
|
The <a href="http://lxr.mozilla.org/seamonkey/source/accessible/">Accessible module</a> is where we implement support for the <a href="http://www.microsoft.com/enable/msaa/">Microsoft Active Accessibility (MSAA) API</a>
|
|
(<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=12952">bug 12952</a>).
|
|
Support for Sun's <a href="http://www.sun.com/access/gnome/">Gnome Accessibility API</a> is part of our future plans as well.
|
|
</p>
|
|
</ul>
|
|
<h2>What is MSAA?</h2>
|
|
<ul>
|
|
<p>
|
|
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 <a href="http://www.audiodata.de/e/produkte/pc/lapbraille/">refreshable braille display</a>.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
MSAA provides information in two different ways:
|
|
<ol>
|
|
<li>a COM interface (IAccessible) that allows applications to expose the tree of data nodes that make up
|
|
each window in the user interface currently being interacted with and</li>
|
|
<li> a set of system messages
|
|
that confer accessibility-related events such as focus changes, changes to document content and alerts.</li>
|
|
</ol>
|
|
</p>
|
|
<p>
|
|
To really learn about MSAA, you need to download
|
|
the entire <a href="http://www.microsoft.com/enable/msaa/download.htm">MSAA SDK</a>.
|
|
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.
|
|
</p>
|
|
</ul>
|
|
<h2>IAccessible Interface</h2>
|
|
<ul>
|
|
<p>
|
|
The IAccessible interface is used in a tree of IAccessible's, each one representing a data node, similar to a DOM.
|
|
</p>
|
|
<p>
|
|
Here are the methods supported in IAccessible:
|
|
<ul>
|
|
<li>get_accParent: Get the parent of an IAccessible.</li>
|
|
<li>get_accChildCount: Get the number of children of an IAccesible.</li>
|
|
<li>get_accChild: Get the child of an Iaccessible.</li>
|
|
<li>get_accName: Get the "name" of the IAccessible, for example the name of a button, checkbox or menuitem.</li>
|
|
<li>get_accValue: Get the "value" of the IAccessible, for example a number in a slider, a URL for a link, the text a user entered in a field.</li>
|
|
<li>get_accDescription: Get a long description of the current IAccessible. This is not really too useful.</li>
|
|
<li>get_accRole: Get an enumerated value representing what this IAccessible is used for, for example.</li>
|
|
is it a link, static text, editable text, a checkbox, or a table cell, etc.</li>
|
|
<li>get_accState: a 32 bit field representing possible on/off states, such as focused, fousable, selected, selectable, visible, protected (for passwords),
|
|
checked, etc. </li>
|
|
<li>get_accHelp: Get context sensitive help for the IAccessible.</li>
|
|
<li>get_accHelpTopic: We don't use this, it's only if the Windows help system is used.</li>
|
|
<li>get_accKeyboardShortcut: What is the keyboard shortcut for this IAccessible.</li>
|
|
<li>get_accFocus: Not sure, why aren't states used for this.</li>
|
|
<li>get_accSelection: Hmm...</li>
|
|
<li>get_accDefaultAction: Get a description or name of the default action for this component, such as "jump" for links.</li>
|
|
|
|
<li>accSelect: Select the item associated with this IAccessible.</li>
|
|
<li>accLocation: Get the x,y coordinates, and the height and width of this IAccessible node.</li>
|
|
<li>accNavigate: Navigate up, down, left or right from this IAccessible.</li>
|
|
<li>accHitTest: Find out what IAccessible exists and a specific coordinate.</li>
|
|
<li>accDoDefaultAction: Perform the action described by get_accDefaultAction.</li>
|
|
<li>put_accName: Change the name.</li>
|
|
<li>put_accValue: Change the value.</li>
|
|
</ul>
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<ul>
|
|
<li>IAccessible (MSAA) is implemented by Accessible, which proxies to nsIAccessible (Mozilla's API)</li>
|
|
<li>IAccessible is also implemented by RootAccessible, representing the root IAccessible for a window,
|
|
and also proxies to an nsIAccessible. In this case nsIAccessible will be implemented by nsRootAccessible.</li>
|
|
</ul>
|
|
<p>
|
|
The first thing that happens when an accessibility aid wants to watch our application is it sends the main application
|
|
window a <a href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a> 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.
|
|
</p>
|
|
<p>
|
|
To create a RootAccessible IAccessible for the window we get a <a href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a> message in, nsWindow.cpp first generates an internal event
|
|
called <a href="http://lxr.mozilla.org/seamonkey/search?string=NS_GETACCESSIBLE">NS_GETACCESSIBLE</a>, 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 <a href="http://lxr.mozilla.org/seamonkey/source/widget/source/windows/Accessible.cpp">
|
|
mozilla/widget/src/windows/Accessible.cpp</a>).
|
|
</P>
|
|
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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).
|
|
</p>
|
|
|
|
</ul>
|
|
<h2>MSAA Events</h2>
|
|
<ul>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
</ul>
|
|
</body>
|
|
</html> |