Update to MSAA support documentation. NPOB.

This commit is contained in:
aaronl%netscape.com 2003-04-28 13:32:08 +00:00
Родитель 5dc7316f87
Коммит a198355723
1 изменённых файлов: 564 добавлений и 168 удалений

Просмотреть файл

@ -1,190 +1,586 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>
How the Accessible Module works
</title>
<title>How the Accessible Module works</title>
</head>
<body>
<h1>How the Accessible module (accessibility.dll) works</h1>
<h1>Explanation of Mozilla's MSAA implementation<br>
and Cheat Sheet for Engineers new to MSAA</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>
<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. This has the details of what we do and do not support.<br>
</p>
<p>Mozilla supports the <a
href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaastart_9w2t.asp?frame=true">Microsoft
Active Accessibility (MSAA) API</a> , used on Windows operating
sytstems. to support assistive technologies for users with disabilities.
The <a href="http://lxr.mozilla.org/seamonkey/source/accessible/">Accessible
module</a> is where we implement the support for MSAA (<a
href="http://bugzilla.mozilla.org/show_bug.cgi?id=12952">bug 12952</a>)
as well as for Sun's <a
href="http://wwws.sun.com/software/star/gnome/accessibility/architecture.html">ATK</a>
API for Linux and UNIX. For documentatin specific to the Mozilla ATK
effort, supported by Sun Microsystems, see the <a
href="http://www.mozilla.org/projects/ui/accessibility/unix/index.html">Mozilla
accessibility on Unix</a> page.<br>
</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>
<p>3rd party assistive technology, such as screen readers, screen
magnifiers amd voice input software, want to track what's happening
inside Mozilla. They needs to know about focus changes and other events,
and it needs to know what objects are contained in the current document
or dialog box. Using this information, a screen reader will speak out
loud important changes to the document or UI, and allow the user to
track where they navigate. The screen reader user can navigate the web
page using screen reader commands or browser commands, and the two
pieces of software must remain in sync. Some screen readers can even
show information on a <a
href="http://www.audiodata.de/e/produkte/pc/lapbraille/">refreshable
braille display</a>. Screen magnifers will zoom to the focus, keeping it
on the screen at all times, or even allow the user to enter a special
low vision document reading mode.&nbsp; Finally, voice dictation
software needs to know what's in the current document or UI in order to
implement "say what you see" style features.<br>
</p>
<p> On Microsoft Windows, these kinds of assistive technology acquire
this necessary information via a comination of&nbsp; hacks, MSAA and
proprietary DOMs. 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: </p>
<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>
<p> To really learn about MSAA, you need to download the entire <a
href="http://msdn.microsoft.com/library/default.asp?URL=/downloads/list/accessibility.asp">MSAA
SDK</a>. Without downloading the SDK, you won't get the very useful
tools, which help a great deal in the learning process. The Accessible
Event Watcher 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, and what the screen boundaries of each object are.<br>
</p>
</ul>
<h2>IAccessible Interface</h2>
<h2>How to hook in / How the IAccessible's are Created</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:
<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.<br>
</p>
<p>&nbsp;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 <a
href="http://lxr.mozilla.org/seamonkey/find?string=msaa/nsDocAccessibleWrap">nsDocAccessibleWrap</a>
for an inner window or <a
href="http://lxr.mozilla.org/seamonkey/find?string=msaa/nsRootAccessibleWrap">nsRootAccessibleWrap</a>
for a top level window. These classes implement both nsIAccessible, our
cross platform API, and IAccessible, which is specific to
Windows/MSAA/COM. The nsRootAccessibleWrap class is then told to listen
for DOM, page load and scroll events.&nbsp; implementation&nbsp; It uses
these events to fire MSAA-specific events such as EVENT_OBJECT_FOCUS or
EVENT_OBJECT_STATECHANGE.. We'll explain more about events in a moment.<br>
</p>
</ul>
<h2>The Accessible Tree and the DOM Tree<br>
</h2>
<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>
<p>After the root or doc accessible for a window has been created and
handed back to the MSAA client, it is used to traverse the rest of the
IAccessible tree using accNavigation, get_accChild and get_accParent.
Any IAccessible will support those methods. We also support
IEnumVARIANT::Next() which allows for fast marshaling of all of an
objects children to a client via COM. In other words, the assistive
technology can say "guve me all 20 children of this object into this
array". That's much faster than 20 separate calls, one for each child.<br>
</p>
<p>In Mozilla, the client has another choice for tree navigation --
it can use the DOM via Mozilla's <a
href="http://lxr.mozilla.org/seamonkey/source/accessible/public/msaa/ISimpleDOMNode.idl">ISimpleDOMNode</a>interface.
An IAccessible can be used to QueryInterface to an ISimpleDOMNode, and
vice versa for a round trip. However, one might QI ISimpleDOMNode to
IAccessible only to find it is null, which means that particular node in
question is not exposed in the IAccessible tree.<br>
</p>
</ul>
<h3>MSAA tree vs. DOM tree - what's the relationship?</h3>
<ul>
<p> <img
src="http://www.mozilla.org/projects/ui/accessibility/images/tree-relativity.gif"
alt="Diagram showing MSAA tree is a subset of the DOM tree"
title="Diagram showing MSAA tree is a subset of the DOM tree"> </p>
The MSAA tree and the DOM tree are parallel structures, although the
MSAA tree is a subset of the DOM tree. <code>QueryInterface()</code> can
be used to switch between the interfaces used in the two trees
(IAccessible and ISimpleDOMNode). If there is no MSAA node for a DOM
node, <code>QueryInterface()</code> will return null.
</ul>
<h2>IAccessible Methods - Cheatsheet for Implementors</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 - a minimal
implementation would contain those marked "<span
style="font-weight: bold;">[important]</span>" :<br>
</p>
<ul>
<li>get_accParent: Get the parent of an IAccessible. <span
style="font-weight: bold;">[important]</span><br>
</li>
<li>get_accChildCount: Get the number of children of an IAccesible. <span
style="font-weight: bold;">[important]</span></li>
<li>get_accChild: Get the child of an IAccessible. <span
style="font-weight: bold;">[important]</span></li>
<li>get_accName: Get the "name" of the IAccessible, for example the
name of a button, checkbox or menuitem. <span style="font-weight: bold;">[important]</span></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. <span style="font-weight: bold;">[important]</span></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. <br>
</li>
is it a link, static text, editable text, a checkbox, or a table
cell, etc. <span style="font-weight: bold;">[important]</span><span
style="font-weight: bold;"></span><li>get_accState: a 32 bit field
representing possible on/off states, such as focused, fousable,
selected, selectable, visible, protected (for passwords), checked, etc. <span
style="font-weight: bold;">[important]</span> </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_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 (underlined alt+combo mnemonic)<br>
</li>
<li>get_accFocus: Which child is focused? <span
style="font-weight: bold;">[important]</span></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>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. <span
style="font-weight: bold;">[important]</span></li>
<li>accLocation: Get the x,y coordinates, and the height and width
of this IAccessible node. <span style="font-weight: bold;">[important]</span></li>
<li>accNavigate: Navigate up, down, left or right from this
IAccessible. <span style="font-weight: bold;">[important]</span></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>
</ul>
</p>
</ul>
<h2>The Basics</h2>
<h2>The Implementations Behind IAccessible</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>
<p>The methods in ns*AccessibleWrap classes don't implement much
accessibility support. These platform-specific classes inherit from
cross-platform classes, where the real implementations exist. For
example, nsAccessibleWrap inherits from nsAccessible. Every accessible
object in the MSAA tree has an nsAccessible class, which exposes
accessibility information through nsIAccessible, in a cross-platform
manner. This base implementation for nsIAccessible knows how to walk
Mozilla's content DOM and frame tree, exposing only the objects that are
needed for accessibility.<br>
</p>
<p>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 nsIAccessible interface is implemented by a variety of
cross-platform 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, and can support both MSAA, ATK and hopefully any
future accessibility API's we need to support.<br>
</p>
<p>The specific implementations of nsIAccessible for each widget or
content type override methods from nsAccessible. For example
nsHTMLButtonAccessible overrides get_accRole to expose ROLE_BUTTON for
that getter. <br>
</p>
<p>A more complicated set of nsIAccessible methods which can be
overriden are GetAccFirstChild/GetAccLastChild/GetAccChildCount, which
allows for objects to define their own decendent subtrees. The default
behavior for nsIAccessible::getAccFirstChild is to instantiate 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. This is necessary
because the image map areas can be in a completely different area of the
DOM from the image they apply to.<br>
</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>
<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 nsDocAccessibleWrap or
nsRootAccessibleWrap for the child IAccessible that matches the window
handle and child id we indicated through NotofyWinEvent(). </p>
<p>In Mozilla, we use the DOM node pointer in the accessible object
as its child ID, which is also used as a hashkey into our cache. During
the callback, we look up the original accessible node in the
nsDocAccessible's cache and return it.<br>
</p>
</ul>
<ul>
<p>Most MSAA events aren't utilized by accessibility aids. Therefore
we implement only the handful that matter. See <a
href="http://www.mozilla.org/projects/ui/accessibility/vendors-win.html">Gecko
Info for Windows Accessibility Vendors</a> for the list of events we
implement. By far the most important one is EVENT_OBJECT_FOCUS.<br>
</p>
</ul>
<h2>MSAA Events - Cheatsheet for Implementors<br>
</h2>
<ul>
<p>For information on what each event does, see the <a
href="file:///c:/moz/mozilla/accessible/Hello">MSDN Event Constants
page</a>.</p>
<p>Check with your assistive technology partners to find out what
events you need to support. There's a very good chance they won't ask
for more than the events marked <span style="font-weight: bold;">[important]</span>:<br>
</p>
</ul>
<table
style="text-align: left; margin-left: auto; width: 75%; margin-right: auto;"
border="0" cellspacing="2" cellpadding="2">
<tbody>
<tr>
<td style="vertical-align: top;">EVENT_SYSTEM_SOUND<br>
EVENT_SYSTEM_ALERT<br>
EVENT_SYSTEM_FOREGROUND<br>
EVENT_SYSTEM_MENUSTART<br>
EVENT_SYSTEM_MENUEND<br>
EVENT_SYSTEM_MENUPOPUPSTART <span style="font-weight: bold;">[important]</span><br>
EVENT_SYSTEM_MENUPOPUPEND <span style="font-weight: bold;">[important]</span><br>
EVENT_SYSTEM_CAPTURESTART<br>
EVENT_SYSTEM_CAPTUREEND<br>
EVENT_SYSTEM_MOVESIZESTART<br>
EVENT_SYSTEM_MOVESIZEEND<br>
EVENT_SYSTEM_CONTEXTHELPSTART<br>
EVENT_SYSTEM_CONTEXTHELPEND<br>
EVENT_SYSTEM_DRAGDROPSTART<br>
EVENT_SYSTEM_DRAGDROPEND<br>
EVENT_SYSTEM_DIALOGSTART<br>
EVENT_SYSTEM_DIALOGEND<br>
EVENT_SYSTEM_SCROLLINGSTART<br>
EVENT_SYSTEM_SCROLLINGEND <span style="font-weight: bold;">[possibly
important, talk to AT vendor]</span><br>
EVENT_SYSTEM_SWITCHSTART<br>
EVENT_SYSTEM_SWITCHEND<br>
<br>
</td>
<td style="vertical-align: top;">EVENT_SYSTEM_MINIMIZESTART<br>
EVENT_SYSTEM_MINIMIZEEND<br>
EVENT_OBJECT_CREATE <span style="font-weight: bold;">[don't implement,
watching system generated versions of this event causes </span><span
style="font-weight: bold;">assistive technology </span><span
style="font-weight: bold;">crashes]</span><br>
EVENT_OBJECT_DESTROY <span style="font-weight: bold;">[don't
implement, watching system generated versions of this event causes
assistive technology crashes]</span><br>
EVENT_OBJECT_SHOW<br>
EVENT_OBJECT_HIDE<br>
EVENT_OBJECT_REORDER <span style="font-weight: bold;">[important for
mutating docs in future, but not yet]</span><br>
EVENT_OBJECT_FOCUS <span style="font-weight: bold;">[important]</span><br>
EVENT_OBJECT_SELECTION<br>
EVENT_OBJECT_SELECTIONADD<br>
EVENT_OBJECT_SELECTIONREMOVE<br>
EVENT_OBJECT_SELECTIONWITHIN<br>
EVENT_OBJECT_STATECHANGE <span style="font-weight: bold;">[important
for checkboxes and radio buttons]</span><br>
EVENT_OBJECT_LOCATIONCHANGE<br>
EVENT_OBJECT_NAMECHANGE<br>
EVENT_OBJECT_DESCRIPTIONCHANGE<br>
EVENT_OBJECT_VALUECHANGE<br>
EVENT_OBJECT_PARENTCHANGE<br>
EVENT_OBJECT_HELPCHANGE<br>
EVENT_OBJECT_DEFACTIONCHANGE<br>
EVENT_OBJECT_ACCELERATORCHANGE<br>
</td>
</tr>
</tbody>
</table>
<h2>Dealing with the Quirks of MSAA</h2>
<p style="margin-left: 40px;">MSAA has a well deseved reputation for
quirkiness. It is not "plug and play", and will take a lot of
testing/refinement before your solution works with any product. Here
are some of it's quirks and some solutions/workarounds:<br>
</p>
<ul>
<li>Crash prone -&nbsp; more than one process refcounting same object</li>
</ul>
<div style="margin-left: 40px;">Problem: When your application closes,
different signals are broadcast. For example, the application window
closes and the window is blurred. It is impossible to know if and when
the 3rd party assistive technology will use one of these signals to
release the objects of yours that are being refcounted. This can lead
to crashes where some of your dll's are unloaded but not others, and a
destructor is called in an unloaded DLL.<br>
<br>
Solution: Create a "shutdown" method for each internal accessible
object, to remove any references to other internal objects before any
of your dll's are unloaded. In order to do this effectively, you will
have to keep track of every accessible object that you create. The
easiest way to do that is to keep a pointer to an accessible in each
internal UI object. If that pointer is non-null, then there is an
accessible object for it. Whenever a UI object is destroyed, shutdown
it's accessible object as well. In Gecko/Mozilla we are not allowed to
keep this extra pointer for each accessible object, so we use a hash
table cache, which is kept in sync with the tree of UI objects. Also,
don't implement EVENT_OBJECT_CREATE or EVENT_OBJECT_DESTROY. Vendors
have found that watching these events causes crashes.<br>
</div>
<ul>
<li>Hacky caret tracking interferes with MSAA solution<br>
</li>
</ul>
<div style="margin-left: 40px;">Problem: The screen reader does not use
the MSAA caret. It follows screen draws, and looks for a vertical
blinking line. Unfortunately, some screen readers can get confused by
the vertical lines on other objects, such as list boxes, even though
those lines are not blinking.<br>
<br>
Solution: Make sure there is a configuration file for each screen
reader specific to your application. Read the manual and find the
keystroke or commands for training the caret, and save this information
in the configuration file.<br>
</div>
<ul>
<li> Event window confusion<br>
<br>
Problem: The screen reader or other assistive technology does not track
the focus or other events correctly.<br>
<br>
Solution: This may be because you are reporting that the events are
occuring in a different window from the system window focus that the
screen reader gets from the Win32 call GetGUIThreadInfo() in the
hwndFocus field. This might be because although you are visibly showing
window focus on the correct window, you have not told the operating
system to change focus to this window. You must keep the two in sync.<br>
<br>
</li>
<li>Confusion with system-generated events<br>
<br>
Problem: When you test with Accessible Event Watcher in the MSAA SDK,
you will see many events that you yourself did not generate. Microsoft
Windows generates some events, like focus events, for you. This is
extremely annoying. The assistive technology has no way to tell whether
the event came from your application or from Windows. For example, when
you set window focus, Windows will generate an EVENT_OBJECT_FOCUS
event. If you happen to set window focus after you fired your own
EVENT_OBJECT_FOCUS event, your event will be ignored, because assitive
techology software tends to pay attention only to the last focus event.<br>
<br>
Solution: When an object is about to get focused in a different window,
make sure you focus a window before you fire your own focus events for
objects inside it. Test using Accessible Event Watcher in the MSAA SDK,
and use the settings to watch subsets of accessibility events. Count on
the assistive technology to make sense out the jumble.<br>
<br>
</li>
<li>No unique child id for object in window.<br>
<br>
Problem: MSAA expects events to be reported using
NotifyWinEvent(eventNum, hWnd, worldID, childID), and there may not be
an obvious way to get a window handle and a 32 bit childID for an
object. You may be using windowless controls, or have an entire
document with lots of objects in a given window. The child ID must be
unique, because this is what the assistive technology will use to
retrieve the accessible via AccessibleObjectFromEvent() which ends up
using get_accChild on the accessible for the given window.<br>
<br>
Solution: In Gecko/Mozilla, we did not want to store an extra 32 bit
unique ID value on every object. Instead, we hand back a 32 bit value
derived from the UI object's pointer, which is unique. We ensure that
the value we hand back is always negative. When the get_accChild call
comes back, we check our hash table cache for that window to see if
there's an accessible object still associated with that unique value.
This means the client must use AccessibleObjectFromEvent immediately,
because there is a danger that the object will go away, and another
differnt object will be created with the same pointer value.<br>
<br>
</li>
<li>Not all features utilized by 3rd party vendors<br>
<br>
Problem: The assistive technology does not use 50% of what's available
in MSAA, e.g. MSAA caret, a lot of events, roles, states and methods.<br>
<br>
Solution: Use this document to see what is generally considered
important by assistive technology manufacturers. Contact the vendors
early and often as you plan and implement your architecture, to see
what's important to them. Ignore every OBJID except for OBJID_CLIENT in
the WM_GETOBJECT message. Don't try to build a palace that no one will
live in. Implement only what's needed.<br>
<br>
</li>
<li>Missing functionality in MSAA<br>
<br>
Problem and Solutions: Assistive technology vendors need some things
which MSAA does not provide, such as:<br>
</li>
<ul>
<li>No way of signifying that a document has finished
loading.&nbsp; Fire EVENT_OBJECT_STATECHANGE for a window/client/pane
object when it starts to load a new document. Use STATE_BUSY to
indicate that a new document is being loaded. When the loading has
finished, fire another EVENT_OBJECT_STATECHANGE event and clear the
STATE_BUSY flag.&nbsp; </li>
</ul>
<ul>
<li>No method to get clipped/unclipped bounds of a piece of text
within a text object. This is needed by screen magnifiers. No scrollTo
method, also needed by screen magnifiers. Implement a custom interface
for text objects, and support it through QueryInterface or QueryService
if it's being implemented on a different object than IAccessible is.
Support a scrollTo method which takes a text index, and a
getClippedBounds and getUnclippedBounds method which takes a start and
end index. Publish your custom interface.</li>
<li>No way for assistive technology to know when scrolling has
stopped. Fire the EVENT_SYSTEM_SCROLLINGEND event to indicate when
scrolling has ended (try not to fire too many of these, wait until
scrolling has truly stopped). There is no need to support
EVENT_SYSTEM_SCROLLINGSTART, it is not used by assistive technology.<br>
</li>
<li>No support for document formatting or "DOM" as requested by
some vendors: support a custom interface that gives them the formatting
information they are requesting.<br>
<br>
</li>
</ul>
<li>Dueling text equivalents<br>
<br>
Problem: There are three kinds of text equivalents, and it is difficult
to know when to use each. Acrobat uses accessible value for text labels
where as most programs use accessible name. There are different roles
for text objects, ROLE_STATICTEXT and ROLE_TEXT (editable text), which
seems to be used for non-editable text in many places.<br>
<br>
Solution: Be as consistent with Internet Explorer as possible. Use
accessible name for most text equivalents, and accessible value for
URL's. Don't use accessible description unless you really do have a
long description for the object you need to expose -- most assistive
technology makes little use of it. Use ROLE_STATICTEXT for labels
specific to dialog controls, and always use ROLE_TEXT for document text
even if the text is not editable (in that case use ROLE_TEXT with
STATE_READONLY).<br>
<br>
</li>
<li>Issues with Links<br>
<br>
Problem: The assistive technology has inflexible heuristics when it
comes to reading links. First, it won't read the object unless the
states are correctly set. Second, it won't read the object if it cannot
parse the whitespace according to its own rules.<br>
<br>
Solution: Make sure the ROLE_LINK object and its child ROLE_TEXT
objects all have STATE_LINKED set. For multi-line links with a line
break in the middle, make sure there is no whitespace at the beginning
or end of any of the accessible names, and make sure there is a \r\n
where the line breaks occur in the accessible name for the ROLE_LINK.
For an example of how to do this properly, see Internet Explorer or
Gecko. Again, if it's not done exactly this way, some links will not be
read.<br>
<br>
</li>
<li>MSAA Implementation is Not Performant<br>
<br>
Problem: The assistive technology may interact slowly with your
application.<br>
<br>
Solution: Try not to calculate the same things more than once or create
the same objects more than once. For example, create and cache an
object's children when you look for them in get_accChildCount(), so
that you can just hand them back when asked for using get_accChild() or
accNavigate(). Support IEnumVARIANT so that the MSAA client can ask for
a number of children in one call. In custom interfaces, create methods
that hand back a lot of data in one call, rather than requiring a large
number of calls. The fewer calls, the better, because a lot of slowness
is caused by COM Marshaling.<br>
<br>
</li>
<li>Differing client implementations<br>
<br>
Problem: Every assistive technology uses MSAA differently.<br>
<br>
Solution: We don't know of any outright conflicts in the differing uses
of MSAA (yet). However, be on guard. If a vendors asks you to do
something different from the spec, it's better to check with the other
vendors before moving forward. Check to see what applications from
Microsoft do in a similar situation.<br>
<br>
</li>
<li>Vendor quirks:<br>
<br>
Problem: Because assistive technology tends to utilize MSAA as an
additional solution resting on top of old hacks, rather than a
completely clean and separate way to deal with an application, and
because of the quirky nature of MSAA and of the inflexible heuristics
that screen readers use, we do not have a "plug and play solution". In
addition, assistive technology vendors are tiny companies, often
scrambling to keep up with changes in the applications they already
support, or new products other than yours which they need to support.
It's very difficult to get vendors to spend time testing an MSAA
implementation, send feedback or help find out why things aren't
working. Time and version commitments often fall through. There is
always a belated new version due around corner, after which you will be
promised to be the next prioritiy.<br>
<br>
Solution: Try to reach out as a friend of the assistive technology
company. Be as easy to work with as you possibly can, including being
extremely responsibe to bug reports with new test builds, as well as
being very communicative about what you have fixed. Do as much work as
you possibly can without their help. See if your organization can offer
something they can't get for themselves. Be patient, set your
expectations to a reasonable level. Realize that it's still business
for these companies, and that they need to sell more copies of their
software to make up for the time they spend on compatibility with your
application. Remember that no matter how small they are, you need them
more than they need you, unless your application's accessibility is
being demanded by end-users.</li>
</ul>
<h2>Feedback</h2>
<div style="margin-left: 40px;">How can this document be improved? Was
it useful? Questions? Contact <a href="mailto:aaronl@netscape.com">aaronl@netscape.com</a><br>
</div>
</body>
</html>