190 строки
11 KiB
HTML
190 строки
11 KiB
HTML
<html>
|
|
<head>
|
|
<title>
|
|
How the Accessible Module works
|
|
</title>
|
|
</head>
|
|
<body>
|
|
<h1>How the Accessible module (accessibility.dll) works</h1>
|
|
<ul>
|
|
<p>
|
|
See also:
|
|
<a href="http://www.mozilla.org/projects/ui/accessibility/vendors-win.html">Gecko Info for Windows Accessibility Vendors</a>, a primer for vendors of 3rd party accessibility software, on how to use our MSAA and other relevant API's.
|
|
</p>
|
|
<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: Which child is focused?</li>
|
|
<li>get_accSelection: Which children of this item are selected?</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>
|
|
</ul>
|
|
<h2>The Basics</h2>
|
|
<ul>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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 <a href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a> 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.
|
|
</p>
|
|
</ul>
|
|
|
|
<h2>How the IAccessible's are Created</h2>
|
|
<ul>
|
|
<p>
|
|
To create the root IAccessible for a window the first time it gets the <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.
|
|
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 <a href="http://lxr.mozilla.org/seamonkey/source/widget/source/windows/Accessible.cpp">
|
|
mozilla/widget/src/windows/Accessible.cpp</a>).
|
|
</P>
|
|
</ul>
|
|
<h2>The Real Power Behind IAccessible's</h2>
|
|
<ul>
|
|
<p>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.
|
|
</h2>
|
|
<p>
|
|
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.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</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 the Win32 call SetWinEventHook() 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 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().
|
|
</p>
|
|
<p>
|
|
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> |