mozilla

Version 140526 von Implementing a Microsoft Active Accessibility (MSAA) Server

  • Adressname der Version: Accessibility/Implementing_an_MSAA_Server
  • Titel der Version: Implementing a Microsoft Active Accessibility (MSAA) Server
  • ID der Version: 140526
  • Erstellt:
  • Autor: TylerD
  • Aktuelle Version? Nein
  • Kommentar 27 words added

Inhalt der Version

Practical Tips for Developers, and How Mozilla Does It

Contents

Note: this document is a bit old, although still useful. It was written when Mozilla supported MSAA only (IAccessible) and did not yet support the IAccessible2 interface extensions to MSAA.  You can find an up-to-date version at http://www.mozilla.org/access/windows/msaa-server.

This document is for people working to support MSAA in an application in order to make it accessible with 3rd party assistive technologies, as well as for hackers wishing to be involved in Mozilla's MSAA support specifically.
You may also wish to read Gecko Info for Windows Accessibility Vendors, a primer for vendors of 3rd party accessibility software, on how MSAA clients can utilize Gecko's MSAA support.

1. Intro: What is MSAA

1. Intro: What is MSAA?

    MSAA is the Microsoft Active Accessibility (MSAA) API , used on Windows operating systems. to support assistive technologies for users with disabilities.

    Third party assistive technology, such as screen readers, screen magnifiers and 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 refreshable braille display. Screen magnifiers 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, with a variety of features such as ticker mode where text is streamed on a single line.  Finally, voice dictation software needs to know what's in the current document or UI in order to implement "say what you see" kinds of features.

    On Microsoft Windows, these kinds of assistive technology acquire this necessary information via a combination of  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 (Gecko does not). This is so commonly done, that no one even bothers to support the MSAA caret, after all the hack is general solution works with pretty much all applications.

    MSAA provides information in several different ways:

    1. 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
    2. Custom interface extensions via interfaces via QueryInterface and QueryService. This can provide assistive technology with contextual information specific to your object model. For example, Gecko support ISimpleDOMNode to provide information about the DOM node for an accessible object.
    3. A set of system messages that confer accessibility-related events such as focus changes, changes to document content and state changes in UI objects like checkboxes.

    To really learn about MSAA, you need to download the entire MSAA SDK. Without downloading the SDK, you won't get the extremely 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. In addition, MSDN has improved their MSAA documentation.

2. Deciding Which MSAA Features to Support

MSAA Methods - Cheat Sheet for Developers

    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 - a minimal implementation would contain those marked "[important]" :

    • get_accParent: Get the parent of an IAccessible. [important]
    • get_accChildCount: Get the number of children of an IAccessible. [important]
    • get_accChild: Get the child of an IAccessible. [important]
    • get_accName: Get the "name" of the IAccessible, for example the name of a button, checkbox or menu item. [important]
    • 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. [important]
    • get_accDescription: Get a long description of the current IAccessible. This is not really too useful.
    • get_accRole: Get an enumerated value representing what this IAccessible is used for, for example.
    • is it a link, static text, editable text, a checkbox, or a table cell, etc. [important]
    • get_accState: a 32 bit field representing possible on/off states, such as focused, focusable, selected, selectable, visible, protected (for passwords), checked, etc. [important]
    • get_accHelp: Get context sensitive help for the IAccessible.
    • get_accHelpTopic: We don't use this, it's only if the Windows help system is used.
    • get_accKeyboardShortcut: What is the keyboard shortcut for this IAccessible (underlined alt+combo mnemonic)
    • get_accFocus: Which child is focused? [important]
    • get_accSelection: Which children of this item are selected?
    • get_accDefaultAction: Get a description or name of the default action for this component, such as "jump" for links.
    • accSelect: Select the item associated with this IAccessible. [important]
    • accLocation: Get the x,y coordinates, and the height and width of this IAccessible node. [important]
    • accNavigate: Navigate to the first/last child, previous/next sibling, up, down, left or right from this IAccessible. [important, but no need to implement up/down/left/right]
    • accHitTest: Find out what IAccessible exists and a specific coordinate.
    • accDoDefaultAction: Perform the action described by get_accDefaultAction.
    • put_accName: Change the name.
    • put_accValue: Change the value.

MSAA Events Cheat Sheet

    For information on what each event does, see the MSDN Event Constants page.

    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 [important]:

EVENT_SYSTEM_SOUND
EVENT_SYSTEM_ALERT
EVENT_SYSTEM_FOREGROUND
EVENT_SYSTEM_MENUSTART
EVENT_SYSTEM_MENUEND
EVENT_SYSTEM_MENUPOPUPSTART [important]
EVENT_SYSTEM_MENUPOPUPEND [important]
EVENT_SYSTEM_CAPTURESTART
EVENT_SYSTEM_CAPTUREEND
EVENT_SYSTEM_MOVESIZESTART
EVENT_SYSTEM_MOVESIZEEND
EVENT_SYSTEM_CONTEXTHELPSTART
EVENT_SYSTEM_CONTEXTHELPEND
EVENT_SYSTEM_DRAGDROPSTART
EVENT_SYSTEM_DRAGDROPEND
EVENT_SYSTEM_DIALOGSTART
EVENT_SYSTEM_DIALOGEND
EVENT_SYSTEM_SCROLLINGSTART
EVENT_SYSTEM_SCROLLINGEND [possibly important, talk to AT vendor]
EVENT_SYSTEM_SWITCHSTART
EVENT_SYSTEM_SWITCHEND
EVENT_SYSTEM_MINIMIZESTART
EVENT_SYSTEM_MINIMIZEEND
EVENT_OBJECT_CREATE [don't implement, watching system generated versions of this event causes assistive technology crashes]
EVENT_OBJECT_DESTROY [don't implement, watching system generated versions of this event causes assistive technology crashes]
EVENT_OBJECT_SHOW
EVENT_OBJECT_HIDE
EVENT_OBJECT_REORDER [important for mutating docs in future, but not yet]
EVENT_OBJECT_FOCUS [important]
EVENT_OBJECT_SELECTION
EVENT_OBJECT_SELECTIONADD
EVENT_OBJECT_SELECTIONREMOVE
EVENT_OBJECT_SELECTIONWITHIN
EVENT_OBJECT_STATECHANGE [important for checkboxes and radio buttons]
EVENT_OBJECT_LOCATIONCHANGE
EVENT_OBJECT_NAMECHANGE
EVENT_OBJECT_DESCRIPTIONCHANGE
EVENT_OBJECT_VALUECHANGE
EVENT_OBJECT_PARENTCHANGE
EVENT_OBJECT_HELPCHANGE
EVENT_OBJECT_DEFACTIONCHANGE
EVENT_OBJECT_ACCELERATORCHANGE

MSAA States Cheat Sheet

    For information on what each state does, see the MSDN State Constants page.

    Check with your assistive technology partners to find out what states you need to support. There's a very good chance they won't ask for more than the states marked [important]:

STATE_UNAVAILABLE [important]
STATE_SELECTED [important]
STATE_FOCUSED [important]
STATE_PRESSED           
STATE_CHECKED [important]
STATE_MIXED
STATE_READONLY [important]
STATE_HOTTRACKED        
STATE_DEFAULT [important]
STATE_EXPANDED [important]
STATE_COLLAPSED [important]
STATE_BUSY [important]
STATE_FLOATING          
STATE_MARQUEED 
STATE_ANIMATED        
STATE_INVISIBLE      
STATE_OFFSCREEN [important]
STATE_SIZEABLE       
STATE_MOVEABLE       
STATE_SELFVOICING    
STATE_FOCUSABLE [important]
STATE_SELECTABLE [important]
STATE_LINKED [important]
STATE_TRAVERSED [important]
STATE_MULTISELECTABLE [important]
STATE_EXTSELECTABLE  
STATE_ALERT_LOW      
STATE_ALERT_MEDIUM   
STATE_ALERT_HIGH     
STATE_PROTECTED [important]
STATE_HASPOPUP

MSAA Roles Cheat Sheet

    For information on what each role does, see the MSDN Role Constants page.

    Check with your assistive technology partners to find out what roles you need to support. There's a very good chance they won't ask for more than the roles marked [important]:
    There is no need to support the objects marked [inserted by system]. Windows will add those objects to your hierarchy for you.

ROLE_TITLEBAR [inserted by system]
ROLE_MENUBAR [important if you don't use native menus]
ROLE_SCROLLBAR
ROLE_GRIP
ROLE_SOUND
ROLE_CURSOR
ROLE_CARET
ROLE_ALERT
ROLE_WINDOW [inserted by system]
ROLE_CLIENT [important]
ROLE_MENUPOPUP [important]
ROLE_MENUITEM [important]
ROLE_TOOLTIP
ROLE_APPLICATION
ROLE_DOCUMENT
ROLE_PANE [important]
ROLE_CHART
ROLE_DIALOG
ROLE_BORDER
ROLE_GROUPING
ROLE_SEPARATOR [important]
ROLE_TOOLBAR
ROLE_STATUSBAR [important]
ROLE_TABLE [important]
ROLE_COLUMNHEADER
ROLE_ROWHEADER
ROLE_COLUMN
ROLE_ROW
ROLE_CELL [important]
ROLE_LINK [important]
ROLE_HELPBALLOON
ROLE_CHARACTER
ROLE_LIST [important]
ROLE_LISTITEM [important]
ROLE_OUTLINE [important]
ROLE_OUTLINEITEM [important]
ROLE_PAGETAB [important]
ROLE_PROPERTYPAGE [important]
ROLE_INDICATOR
ROLE_GRAPHIC [important]
ROLE_STATICTEXT [important]
ROLE_TEXT [important]
ROLE_PUSHBUTTON [important]
ROLE_CHECKBUTTON [important]
ROLE_RADIOBUTTON [important]
ROLE_COMBOBOX [important]
ROLE_DROPLIST [important]
ROLE_PROGRESSBAR [important]
ROLE_DIAL
ROLE_HOTKEYFIELD
ROLE_SLIDER
ROLE_SPINBUTTON
ROLE_DIAGRAM
ROLE_ANIMATION
ROLE_EQUATION
ROLE_BUTTONDROPDOWN
ROLE_BUTTONMENU
ROLE_BUTTONDROPDOWNGRID
ROLE_WHITESPACE
ROLE_PAGETABLIST [important]
ROLE_CLOCK
ROLE_SPLITBUTTON
ROLE_IPADDRESS
ROLE_NOTHING

MSAA Object Identifiers Cheat Sheet

For information on what each object identifier does, see the MSDN Object Identifiers Constants page.

Check with our assistive technology partners to find out what object identifiers you need to support. There's a very good chance they won't ask for more than the object itentifiers marked [important]:
OBJID_ALERT
OBJID_CARET
OBJID_CLIENT [important]
OBJID_CURSOR
OBJID_HSCROLL
OBJID_NATIVEOM [important? might be useful for supporting custom interfaces, need to research]
OBJID_MENU
OBJID_QUERYCLASSNAMEIDX
OBJID_SIZEGRIP
OBJID_SOUND
OBJID_SYSMENU
OBJID_TITLEBAR
OBJID_VSCROLL
OBJID_WINDOW

3. Dealing with the Quirks of MSAA

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 its quirks and some solutions/workarounds:

<big>MSAA can be crash prone</big>

Problem: Many of MSAA's crash occur because more than one process is refcounting the same objects, and because pointers are being shared between processes. When your application closes, different signals are typically 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 is is refcounting. This can lead to crashes where it releases something and the wrong time, when some of your dll's are unloaded but not others, and a destructor is called in an unloaded DLL.

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 shutdown method for an accessibility object should be called whenever the document or UI object it refers to goes away. 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 the UI object is destroyed, shutdown its accessible object as well. In Gecko/Mozilla we are not allowed to keep this extra pointer for each accessible object, so when accessibility is turned on we use a hash table to cache these objects. Such a cache must be kept in perfect sync with the tree of UI and document objects, which is difficult. Therefore, unless 4 bytes extra on each object is criticial in your application, just keep the extra pointer around instead of using a hash table.

Also, don't implement EVENT_OBJECT_CREATE or EVENT_OBJECT_DESTROY. Vendors have found that watching these events causes crashes.
<big>Hacky caret tracking causes problems
</big>

Problem: Assistive technologies do not use the MSAA caret. They follow screen draws, looking for a vertical blinking line. Unfortunately, some products can get confused by the vertical lines on other objects, such as list boxes, even though those lines are not blinking. The assistive technology may not see your caret at all.

Solution: Make sure there is a configuration file for each assistive technology specific to your application. Read the manual or help, and find the keystroke or commands for training the caret, and save this information in the configuration file. Don't support the MSAA caret, none of the vendors use it.
<big>Event window handle is incorrect</big>

Problem: The screen reader or other assistive technology does not track the focus or other events correctly.

Solution: This may be because you are reporting that the events in a different window from the current system focused. The assistive technology may be asking GetGUIThreadInfo for its hwndFocus, and throwing away MSAA events that are not in the currently focused window. Even if you are visibly showing window focus on the correct window, you must also tell the operating system to focus this window before any other accessibility events get fired in it.

<big>Confusion with system-generated events</big>

Problem: When you test with Accessible Event Watcher in the MSAA SDK, you will see many events that your application did not generate. Microsoft Windows generates some 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 an a ROLE_WINDOW object it also generated for you. If you happen to set window focus after you fired your own EVENT_OBJECT_FOCUS event on an object in the widnow, your event will be ignored, because assistive technology software tends to pay attention only to the last focus event.

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 panel to watch subsets of accessibility events. Count on the assistive technology to make sense out the jumble of extra system-generated events, it's not your problem.

<big>No unique child ID for event target in window</big>

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.

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 different object will be created with the same pointer value.That case seems extremely remote, because information from events is generally retrieved right after the event.

If you're not using a hash table to keep track of unique ID's, store the child ID's and objects for the last 50 or so events in a circular array. In practice, this is enough to keep AccessibleObjectFromEvent() happy.

<big>Not all MSAA features utilized by 3rd party vendors</big>

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. It's difficult to know which things to support.

Solution: Use this document to see what is generally considered important by assistive technology manufacturers. Contact the the top vendors early and often as you plan and implement your architecture, to see what's important to them. Implement only what's needed -- supporting everything would take too long for zero results.

<big>Missing functionality in MSAA</big>

Problem and solutions: Assistive technology vendors need some things which MSAA does not provide, such as:
    • No way of signifying that a document has finished loading.  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. 
    • 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.
    • 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.
    • 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.
<big>Dueling text equivalents</big>

Problem: There are three kinds of text equivalents, and it is difficult to know when to use each. Different applications behave differently in this regard. For example, 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.

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 and UI 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).

<big>Issues with Links</big>

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 can mishandle the object if it cannot parse the whitespace according to its own rules.

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.

<big>MSAA Implementation is Not Performant</big>

Problem: The assistive technology may interact slowly with your application.

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. Fewer calls are much better better because COM Marshaling is slow.

<big>Differing client implementations</big>

Problem: Every assistive technology uses MSAA differently.

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.

<big>Undocumented Window Class Usage</big>

Problem: most assistive technologies won't use your MSAA implementation out of the box. They must list your window classes somewhere in their implementation, and then turn on MSAA support when a window of that class receives focus. The window class is also used to determine a host of hard-coded behaviors, such as whether or not a screen reader will load the entire MSAA tree into a special buffer for the user to navigate with screen reader commands. This is only supposed to occur for document navigation, not for UI/dialogs. where your application's keyboard commands will be solely used to navigate.

Solution: Contact each vendor and let them know what window classes you will be using MSAA for. If possible, use a different window class name for documents/content than you use for UI/dialogs. Or, do what Mozilla does  - expose a control ID (GWL_ID) of 1 for content, and 0 for UI. Consistent window class names are important for the assistive technology vendors, so that they can determine what code to run for a given window. Don't change window class names after you have shipped a version.

<big>Vendor quirks</big>

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 priority.

Solution: Try to reach out in a friendly manner to the assistive technology company. Be as easy to work with as you possibly can -- this includes being extremely responsive to their bug reports with new test builds, as well as being very communicative about what you have changed and when. 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, and set your expectations to a reasonable level. Realize that it's about both pride and revenue for these companies, and that they need to sell a lot of copies of their software to make up the work they put in to support your app. 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.

4. Example: How Gecko and Mozilla Implement MSAA

The Accessible module is where the Mozilla MSAA implementation lives. Feel free to peruse the source code in the accessible module whenever you want to see how something can be implemented.

The accessible module is also where support for Sun's ATK accessibility API for Linux and UNIX is implemented. For documentation specific to the Mozilla ATK effort, supported by Sun Microsystems, see the Mozilla accessibility on Unix page.

Creation of IAccessible Objects

    The first thing that happens when an assistive technology wants to watch our application is that calls the Windows API function AccessibleObjectFromWindow(). This usually happens right after a window gets focused.

    When the WIN32 API function AccessibleObjectFromWindow() is called, Windows sends the window in question a WM_GETOBJECT message requesting an IAccessible for your root object in the window. In our case, this event is received in mozilla/widget/src/windows/nsWindow.cpp. We send back an IAccessible pointer which can be used by the client to get information about this root object. The assistive technology will use that root IAccessible to traverse the rest of the object tree, by navigating to children and then siblings, etc. Every navigation function such as accNavigate(), get_accChild() and get_accParent() returns an IAccessible pointer.

    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 PresShell::HandleEventInternal() via the creation of an nsDocAccessibleWrap for an inner window or nsRootAccessibleWrap for a top level window. These classes implement both nsIAccessible, our cross platform API, as well as IAccessible, which is specific to Windows/MSAA/COM. The cross-platform nsDocAccessible and nsRootAccessible classes they inherit from are then told to start listening for DOM, page load and scroll events.  These events cause MSAA-specific events, such as EVENT_OBJECT_FOCUS or EVENT_OBJECT_STATECHANGE, to fire on UI and document objects within the applicable window. We'll explain more about events later in this section.

    Until the WM_GETOBJECT message is processed, the Gecko accessibility service is not used, and thus the accessibility.dll is not loaded, so there is almost zero overhead for accessibility API support in Mozilla or Gecko, in the general case. Once the accessibility service is created, however, Gecko loads code to create an object on demand for every UI or document object that should support IAccessible. The created objects are cached in a hash table, and shutdown when they're no longer needed. They may still exist in memory in a nonfunctional state until the assistive technology completely releases them. See the section on accessible roles to see what kinds of objects Gecko support IAccessible for.

The Accessible Tree vs. the DOM Tree

    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 "give me all 20 children of this object into this array". That's much faster than 20 separate calls, one for each child.

    In Mozilla, the client has another choice for tree navigation -- it can utilize data stored in the DOM via Mozilla's custom ISimpleDOMNode COM interface. Any 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. See the following diagram for examples of nodes that do no support IAccessible.

MSAA tree vs. DOM tree - what's the relationship?

    Diagram showing MSAA tree is a subset of the DOM tree

    The MSAA tree and the DOM tree are parallel structures, although the MSAA tree is a subset of the DOM tree. QueryInterface() 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,  pAccessible->QueryInterface(IID_IAccessible) will return null.

A Variety of Implementations for IAccessible

There are two main kinds of classes in Mozilla's accessibility class hierarchy, platform-specifc and cross-platform. All of the platform-specific classes have the word "Wrap" appended to them. The Wrap classes contain implementations and interfaces specific to MSAA or ATK. These platform-specific classes inherit from cross-platform classes, where the most of the implementation is done. For example, nsAccessibleWrap inherits from nsAccessible. Every accessible object in the MSAA tree has an implementation dertived from nsAccessible, which exposes accessibility information through nsIAccessible, in a generic cross-platform manner.

This default implementation for nsIAccessible knows how to use nsAccessibleTreeWalker to walk Mozilla's content DOM and frame tree, exposing only the objects that are needed for accessibility. The nsAccessibleTreeWalker class knows what it needs to expose by asking each DOM node's primary frame (a Gecko formatting object) for an nsIAccessible, using the nsIFrame::GetAccessible() method. If nsAccessibleTreeWalker gets an nsIAccessible back, then the DOM node considered to be an accessible object. The nsIAccessible that is returned is either a new one, or reused from the accessibility cache, and the correct type of accessibility object to correctly expose that DOM node through the cross-platform nsIAccessible and MSAA-specific IAccessible interfaces.

Every accessibility object created must be cached, and must inherit from nsAccessibleWrap so that it supports a base implementation of nsIAccessible and IAccessible. Apart from that, it is free to override IAccessible or nsIAccessible methods. In this way each class is tailored to the specific abilities and properties of the HTML or XUL/UI objects it applies to, and can support both MSAA, ATK and hopefully any future accessibility API's we need to support. For example nsHTMLButtonAccessible overrides nsIAccessible::GetAccRole to expose ROLE_BUTTON for IAccessible::get_accRole which uses that.

    A more complicated set of nsIAccessible methods which can be overridden are GetAccFirstChild/GetAccLastChild/GetAccChildCount, which allows for objects to define their own decendant 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.

Generating MSAA Events

    First, keep in mind that most MSAA events aren't utilized by accessibility aids. Therefore we implement only the handful that matter. See the Events cheat sheet above for the list of events we implement. By far the most important one is EVENT_OBJECT_FOCUS.

    When a potential accessibility-related event occurs within Mozilla, it is typically listened for by nsDocAccessible or nsRootAccessible. The event listeners on these classes call FireToolkitEvent(), which is implemented for every accessible. Eventually, the event ends up at nsDocAccessibleWrap::FireToolkitEvent() which calls NotifyWinEvent from the Win32 API. NotifyWinEvent is passed arguments for the window the event occurred in, and the ID of the child within that window. Accessibility aids use the Win32 call SetWinEventHook() to register as a listener for these events. Creating a unique child ID for every object within a window can be difficult, see the problem and solution for no unique child ID for object in window.

    The assistive technology chooses which events it is interested in learning more about by calling the Win32 method AccessibleObjectFromEvent, which returns the IAccessible to the node corresponding to the child number that had been indicated from NotifyWinEvent(). This ends up asking nsDocAccessibleWrap::get_accChild() for a child IAccessible which matches the child ID we indicated through NotifyWinEvent().

    In Mozilla, we use the DOM node pointer in the accessible object as a basis for its child ID, which is also used as a hash key into our cache. We also negate the 32 bit value so that it is always <0, telling us that they're really looking for the IAccessible for an event, not child number x. During the callback, we look up the original accessible node in the nsDocAccessible's cache and return it.

Quelltext der Version

<h2>Practical Tips for Developers, and How Mozilla Does It</h2>
<h2>Contents</h2>
<div style="margin-left: 40px;">
<div class="note">Note: this document is a bit old, although still useful. It was written when Mozilla supported MSAA only (IAccessible) and did not yet support the IAccessible2 interface extensions to MSAA.  You can find an up-to-date version at <a class="external" href="http://www.mozilla.org/access/windows/msaa-server" title="http://www.mozilla.org/access/windows/msaa-server">http://www.mozilla.org/access/windows/msaa-server</a>.</div>
<p>This document is for people working to support MSAA in an application in order to make it accessible with 3rd party assistive technologies, as well as for hackers wishing to be involved in Mozilla's MSAA support specifically.<br>
You may also wish to read <a class="external" 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 MSAA clients can utilize Gecko's MSAA support.</p>
<a class="internal" href="/#intro" title="#intro">1. Intro: What is MSAA</a></div>
<div style="margin-left: 40px;"><a class="internal" href="#cheatsheets" title="#cheatsheets">2. Deciding Which MSAA Features to Support</a><br>
<div style="margin-left: 40px;"><a href="#methods">Methods</a><br>
<a href="#events">Events</a><br>
<a href="#states">States</a><br>
<a href="#roles">Roles</a><br>
<a href="#objid">Object Identifiers</a></div>
<a href="#quirks">3. </a><a href="#quirks">MSAA's Quirks and Workarounds</a><br>
<div style="margin-left: 40px;"><a href="#Crash_prone">MSAA can be crash prone</a><br>
<a href="#Hacky_caret_tracking_not_working">Hacky caret tracking not working</a><br>
<a href="#Event_window_confusion">Event window confusion</a><br>
<a href="#Confusion_with_system-generated_events">Confusion with system-generated events</a><br>
<a href="#Hacky_caret_tracking_not_working">No unique child ID for object in window</a><br>
<a href="#Not_all_MSAA_features_utilized_by_3rd">Not all MSAA features utilized by 3rd party vendors</a><br>
<a href="#Missing_functionality_in_MSAA">Missing functionality in MSAA</a><br>
<a href="#Dueling_text_equivalents">Dueling text equivalents</a><br>
<a href="#Issues_with_Links">Issues with Links</a><br>
<a href="#MSAA_Implementation_is_Not_Performant">Performance Problems</a><br>
<a href="#Differing_client_implementations">Differing client implementations</a><br>
<a href="#Undocumented_Window_Class_Usage">Undocumented Window Class Usage</a><br>
<a href="#Vendor_quirks">Vendor quirks</a></div>
<a class="external" href="file:///F:/Patches/accessible-docs.html#geckoimpl">4. Example: How Gecko and Mozilla Implement MSAA</a><br>
<div style="margin-left: 40px;"><a class="external" href="file:///F:/Patches/accessible-docs.html#Creation_of_IAccessible_Objects">Creation of IAccessible Objects</a><br>
<a href="#The_Accessible_Tree_vs._the_DOM_Tree">The Accessible Tree vs. the DOM Tree</a><br>
<a href="#The_Implementations_Behind_IAccessible">The Various Implementations of IAccessible</a><br>
<a href="#Generating_MSAA_Events">Generating MSAA Events</a></div>
</div>
<h2>1. Intro: What is MSAA?</h2>
<ul> <p>MSAA is the <a class="external" 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 systems. to support assistive technologies for users with disabilities.</p> <p>Third party assistive technology, such as screen readers, screen magnifiers and 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 class="external" href="http://www.deafblind.com/display.html">refreshable braille display</a>. Screen magnifiers 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, with a variety of features such as ticker mode where text is streamed on a single line.  Finally, voice dictation software needs to know what's in the current document or UI in order to implement "say what you see" kinds of features.<br> <br> On Microsoft Windows, these kinds of assistive technology acquire this necessary information via a combination of  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 (Gecko does not). This is so commonly done, that no one even bothers to support the MSAA caret, after all the hack is general solution works with pretty much all applications.</p> <p>MSAA provides information in several 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>Custom interface extensions via interfaces via QueryInterface and QueryService. This can provide assistive technology with contextual information specific to your object model. For example, Gecko support ISimpleDOMNode to provide information about the DOM node for an accessible object.</li> <li>A set of system messages that confer accessibility-related events such as focus changes, changes to document content and state changes in UI objects like checkboxes.</li> </ol> <p>To really learn about MSAA, you need to download the entire <a class="external" 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 extremely 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. In addition, MSDN has improved their <a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaastart_9w2t.asp">MSAA documentation</a>.</p>
</ul>
<h2>2. Deciding Which MSAA Features to Support</h2>
<h2 style="margin-left: 40px;">MSAA Methods - Cheat Sheet for Developers</h2>
<ul style="margin-left: 40px;"> <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>" :</p> <ul> <li>get_accParent: Get the parent of an IAccessible. <span style="font-weight: bold;">[important]</span></li> <li>get_accChildCount: Get the number of children of an IAccessible. <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 menu item. <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.</li> is it a link, static text, editable text, a checkbox, or a table cell, etc. <span style="font-weight: bold;">[important]</span> <li>get_accState: a 32 bit field representing possible on/off states, such as focused, focusable, 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 (underlined alt+combo mnemonic)</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. <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]<br> </span></li> <li>accNavigate: Navigate to the first/last child, previous/next sibling, up, down, left or right from this IAccessible. <span style="font-weight: bold;">[important, </span><span style="font-weight: bold;">but no need to implement up/down/left/right</span><span style="font-weight: bold;">]</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>
<h2 style="margin-left: 40px;">MSAA Events Cheat Sheet</h2>
<ul style="margin-left: 40px;"> <p>For information on what each event does, see the <a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">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>:</p>
</ul>
<table border="0" cellpadding="2" cellspacing="2" style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"> <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> EVENT_SYSTEM_MINIMIZESTART<br> EVENT_SYSTEM_MINIMIZEEND</td> <td style="vertical-align: top;">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</td> </tr> </tbody>
</table>
<h2 style="margin-left: 40px;">MSAA States Cheat Sheet</h2>
<ul style="margin-left: 40px;"> <p>For information on what each state does, see the <a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN State Constants page</a>.</p> <p>Check with your assistive technology partners to find out what states you need to support. There's a very good chance they won't ask for more than the states marked <span style="font-weight: bold;">[important]</span>:</p>
</ul>
<table border="0" cellpadding="2" cellspacing="2" style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"> <tbody> <tr> <td style="vertical-align: top;">STATE_UNAVAILABLE <span style="font-weight: bold;">[important]</span><br> STATE_SELECTED <span style="font-weight: bold;">[important]</span><br> STATE_FOCUSED <span style="font-weight: bold;">[important]</span><br> STATE_PRESSED           <br> STATE_CHECKED <span style="font-weight: bold;">[important]</span><br> STATE_MIXED<br> STATE_READONLY <span style="font-weight: bold;">[important]</span><br> STATE_HOTTRACKED         <br> STATE_DEFAULT <span style="font-weight: bold;">[important]</span><br> STATE_EXPANDED <span style="font-weight: bold;">[important]</span><br> STATE_COLLAPSED <span style="font-weight: bold;">[important]</span><br> STATE_BUSY <span style="font-weight: bold;">[important]</span><br> STATE_FLOATING          <br> STATE_MARQUEED  <br> STATE_ANIMATED         <br> STATE_INVISIBLE      </td> <td style="vertical-align: top;">STATE_OFFSCREEN <span style="font-weight: bold;">[important]</span><br> STATE_SIZEABLE       <br> STATE_MOVEABLE       <br> STATE_SELFVOICING    <br> STATE_FOCUSABLE <span style="font-weight: bold;">[important]</span><br> STATE_SELECTABLE <span style="font-weight: bold;">[important]</span><br> STATE_LINKED <span style="font-weight: bold;">[important]</span><br> STATE_TRAVERSED <span style="font-weight: bold;">[important]</span><br> STATE_MULTISELECTABLE <span style="font-weight: bold;">[important]</span><br> STATE_EXTSELECTABLE  <br> STATE_ALERT_LOW      <br> STATE_ALERT_MEDIUM   <br> STATE_ALERT_HIGH     <br> STATE_PROTECTED <span style="font-weight: bold;">[important]</span><br> STATE_HASPOPUP</td> </tr> </tbody>
</table>
<h2 style="margin-left: 40px;">MSAA Roles Cheat Sheet</h2>
<ul style="margin-left: 40px;"> <p>For information on what each role does, see the <a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN Role Constants page</a>.</p> <p>Check with your assistive technology partners to find out what roles you need to support. There's a very good chance they won't ask for more than the roles marked <span style="font-weight: bold;">[important]</span>:<br> There is no need to support the objects marked <span style="font-weight: bold;">[inserted by system]. </span>Windows will add those objects to your hierarchy for you.</p>
</ul>
<table border="0" cellpadding="2" cellspacing="2" style="text-align: left; width: 75%; margin-right: auto; margin-left: auto;"> <tbody> <tr> <td style="vertical-align: top;">ROLE_TITLEBAR <span style="font-weight: bold;">[inserted by system]</span><br> ROLE_MENUBAR <span style="font-weight: bold;">[important if you don't use native menus]</span><br> ROLE_SCROLLBAR<br> ROLE_GRIP<br> ROLE_SOUND<br> ROLE_CURSOR<br> ROLE_CARET<br> ROLE_ALERT<br> ROLE_WINDOW <span style="font-weight: bold;">[inserted by system]</span><br> ROLE_CLIENT <span style="font-weight: bold;">[important]</span><br> ROLE_MENUPOPUP <span style="font-weight: bold;">[important]</span><br> ROLE_MENUITEM <span style="font-weight: bold;">[important]</span><br> ROLE_TOOLTIP<br> ROLE_APPLICATION<br> ROLE_DOCUMENT<br> ROLE_PANE <span style="font-weight: bold;">[important]</span><br> ROLE_CHART<br> ROLE_DIALOG<br> ROLE_BORDER<br> ROLE_GROUPING<br> ROLE_SEPARATOR <span style="font-weight: bold;">[important]</span><br> ROLE_TOOLBAR<br> ROLE_STATUSBAR <span style="font-weight: bold;">[important]</span><br> ROLE_TABLE <span style="font-weight: bold;">[important]</span><br> ROLE_COLUMNHEADER<br> ROLE_ROWHEADER<br> ROLE_COLUMN<br> ROLE_ROW<br> ROLE_CELL <span style="font-weight: bold;">[important]</span><br> ROLE_LINK <span style="font-weight: bold;">[important]</span><br> ROLE_HELPBALLOON<br> ROLE_CHARACTER</td> <td style="vertical-align: top;">ROLE_LIST <span style="font-weight: bold;">[important]</span><br> ROLE_LISTITEM <span style="font-weight: bold;">[important]</span><br> ROLE_OUTLINE <span style="font-weight: bold;">[important]</span><br> ROLE_OUTLINEITEM <span style="font-weight: bold;">[important]</span><br> ROLE_PAGETAB <span style="font-weight: bold;">[important]</span><br> ROLE_PROPERTYPAGE <span style="font-weight: bold;">[important]</span><br> ROLE_INDICATOR<br> ROLE_GRAPHIC <span style="font-weight: bold;">[important]</span><br> ROLE_STATICTEXT <span style="font-weight: bold;">[important]</span><br> ROLE_TEXT <span style="font-weight: bold;">[important]</span><br> ROLE_PUSHBUTTON <span style="font-weight: bold;">[important]</span><br> ROLE_CHECKBUTTON <span style="font-weight: bold;">[important]</span><br> ROLE_RADIOBUTTON <span style="font-weight: bold;">[important]</span><br> ROLE_COMBOBOX <span style="font-weight: bold;">[important]</span><br> ROLE_DROPLIST <span style="font-weight: bold;">[important]</span><br> ROLE_PROGRESSBAR <span style="font-weight: bold;">[important]</span><br> ROLE_DIAL<br> ROLE_HOTKEYFIELD<br> ROLE_SLIDER<br> ROLE_SPINBUTTON<br> ROLE_DIAGRAM<br> ROLE_ANIMATION<br> ROLE_EQUATION<br> ROLE_BUTTONDROPDOWN<br> ROLE_BUTTONMENU<br> ROLE_BUTTONDROPDOWNGRID<br> ROLE_WHITESPACE<br> ROLE_PAGETABLIST <span style="font-weight: bold;">[important]</span><br> ROLE_CLOCK<br> ROLE_SPLITBUTTON<br> ROLE_IPADDRESS<br> ROLE_NOTHING</td> </tr> </tbody>
</table>
<h2 style="margin-left: 40px;">MSAA Object Identifiers Cheat Sheet</h2>
<p style="margin-left: 80px;">For information on what each object identifier does, see the <a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp">MSDN Object Identifiers Constants page</a>.</p>
<div style="margin-left: 80px;">Check with our assistive technology partners to find out what object identifiers you need to support. There's a very good chance they won't ask for more than the object itentifiers marked <span style="font-weight: bold;">[important]</span>:</div>
<dl style="margin-left: 120px;"><dt>OBJID_ALERT<br>
OBJID_CARET<br>
OBJID_CLIENT <span style="font-weight: bold;">[important]</span><br>
OBJID_CURSOR<br>
OBJID_HSCROLL<br>
OBJID_NATIVEOM <span style="font-weight: bold;">[important? might be useful for supporting custom interfaces, need to research]</span><br>
OBJID_MENU<br>
OBJID_QUERYCLASSNAMEIDX<br>
OBJID_SIZEGRIP<br>
OBJID_SOUND<br>
OBJID_SYSMENU<br>
OBJID_TITLEBAR<br>
OBJID_VSCROLL<br>
OBJID_WINDOW<br>
</dt></dl>
<h2>3. 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 its quirks and some solutions/workarounds:</p>
<div style="margin-left: 40px;"><big>MSAA can be crash prone</big></div>
<div style="margin-left: 80px;"><br>
<span style="text-decoration: underline;">Problem</span>: Many of MSAA's crash occur because more than one process is refcounting the same objects, and because pointers are being shared between processes. When your application closes, different signals are typically 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 is is refcounting. This can lead to crashes where it releases something and the wrong time, when some of your dll's are unloaded but not others, and a destructor is called in an unloaded DLL.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: 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 shutdown method for an accessibility object should be called whenever the document or UI object it refers to goes away. 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 the UI object is destroyed, shutdown its accessible object as well. In Gecko/Mozilla we are not allowed to keep this extra pointer for each accessible object, so when accessibility is turned on we use a hash table to cache these objects. Such a cache must be kept in perfect sync with the tree of UI and document objects, which is difficult. Therefore, unless 4 bytes extra on each object is criticial in your application, just keep the extra pointer around instead of using a hash table.<br>
<br>
Also, don't implement EVENT_OBJECT_CREATE or EVENT_OBJECT_DESTROY. Vendors have found that watching these events causes crashes.</div>
<div style="margin-left: 40px;"><big>Hacky caret tracking causes problems<br>
</big></div>
<div style="margin-left: 80px;"><br>
<span style="text-decoration: underline;">Problem</span>: Assistive technologies do not use the MSAA caret. They follow screen draws, looking for a vertical blinking line. Unfortunately, some products can get confused by the vertical lines on other objects, such as list boxes, even though those lines are not blinking. The assistive technology may not see your caret at all.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: Make sure there is a configuration file for each assistive technology specific to your application. Read the manual or help, and find the keystroke or commands for training the caret, and save this information in the configuration file. Don't support the MSAA caret, none of the vendors use it.</div>
<div style="margin-left: 40px;"><big>Event window handle is incorrect</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: The screen reader or other assistive technology does not track the focus or other events correctly.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: This may be because you are reporting that the events in a different window from the current system focused. The assistive technology may be asking GetGUIThreadInfo for its hwndFocus, and throwing away MSAA events that are not in the currently focused window. Even if you are visibly showing window focus on the correct window, you must also tell the operating system to focus this window before any other accessibility events get fired in it.</div>
<br>
<big>Confusion with system-generated events</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: When you test with Accessible Event Watcher in the MSAA SDK, you will see many events that your application did not generate. Microsoft Windows generates some 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 an a ROLE_WINDOW object it also generated for you. If you happen to set window focus after you fired your own EVENT_OBJECT_FOCUS event on an object in the widnow, your event will be ignored, because assistive technology software tends to pay attention only to the last focus event.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: 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 panel to watch subsets of accessibility events. Count on the assistive technology to make sense out the jumble of extra system-generated events, it's not your problem.</div>
<br>
<big>No unique child ID for event target in window</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: 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>
<span style="text-decoration: underline;">Solution</span>: 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 different object will be created with the same pointer value.That case seems extremely remote, because information from events is generally retrieved right after the event.<br>
<br>
If you're not using a hash table to keep track of unique ID's, store the child ID's and objects for the last 50 or so events in a circular array. In practice, this is enough to keep AccessibleObjectFromEvent() happy.</div>
<br>
<big>Not all MSAA features utilized by 3rd party vendors</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: 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. It's difficult to know which things to support.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: Use this document to see what is generally considered important by assistive technology manufacturers. Contact the the top vendors early and often as you plan and implement your architecture, to see what's important to them. Implement only what's needed -- supporting everything would take too long for zero results.</div>
<br>
<big>Missing functionality in MSAA</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem and solutions:</span> Assistive technology vendors need some things which MSAA does not provide, such as:</div>
</div>
<ul style="margin-left: 40px;"> <ul> <li>No way of signifying that a document has finished loading.  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. </li> <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.</li> </ul> <ul> <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.</li> </ul>
</ul>
<div style="margin-left: 40px;"><big>Dueling text equivalents</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;"> Problem</span>: There are three kinds of text equivalents, and it is difficult to know when to use each. Different applications behave differently in this regard. For example, 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>
<span style="text-decoration: underline;">Solution</span>: 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 and UI 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).</div>
<br>
<big>Issues with Links</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: 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 can mishandle the object if it cannot parse the whitespace according to its own rules.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: 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.</div>
<br>
<big>MSAA Implementation is Not Performant</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;"> Problem</span>: The assistive technology may interact slowly with your application.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: 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. Fewer calls are much better better because COM Marshaling is slow.</div>
<br>
<big>Differing client implementations</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: Every assistive technology uses MSAA differently.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: 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.</div>
<br>
<big>Undocumented Window Class Usage</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: most assistive technologies won't use your MSAA implementation out of the box. They must list your window classes somewhere in their implementation, and then turn on MSAA support when a window of that class receives focus. The window class is also used to determine a host of hard-coded behaviors, such as whether or not a screen reader will load the entire MSAA tree into a special buffer for the user to navigate with screen reader commands. This is only supposed to occur for document navigation, not for UI/dialogs. where your application's keyboard commands will be solely used to navigate.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: Contact each vendor and let them know what window classes you will be using MSAA for. If possible, use a different window class name for documents/content than you use for UI/dialogs. Or, do what Mozilla does  - expose a control ID (GWL_ID) of 1 for content, and 0 for UI. Consistent window class names are important for the assistive technology vendors, so that they can determine what code to run for a given window. Don't change window class names after you have shipped a version.</div>
<br>
<big>Vendor quirks</big><br>
<br>
<div style="margin-left: 40px;"><span style="text-decoration: underline;">Problem</span>: 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 priority.<br>
<br>
<span style="text-decoration: underline;">Solution</span>: Try to reach out in a friendly manner to the assistive technology company. Be as easy to work with as you possibly can -- this includes being extremely responsive to their bug reports with new test builds, as well as being very communicative about what you have changed and when. 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, and set your expectations to a reasonable level. Realize that it's about both pride and revenue for these companies, and that they need to sell a lot of copies of their software to make up the work they put in to support your app. 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.</div>
</div>
<h2>4. Example: How Gecko and Mozilla Implement MSAA</h2>
<p style="margin-left: 40px;">The <a class="external" href="http://lxr.mozilla.org/seamonkey/source/accessible/">Accessible module</a> is where the Mozilla MSAA implementation lives. Feel free to <a class="external" href="http://lxr.mozilla.org/seamonkey/source/accessible/">peruse the source code in the accessible module</a> whenever you want to see how something can be implemented.</p>
<p style="margin-left: 40px;">The accessible module is also where support for Sun's <a class="external" href="http://wwws.sun.com/software/star/gnome/accessibility/architecture.html">ATK</a> accessibility API for Linux and UNIX is implemented. For documentation specific to the Mozilla ATK effort, supported by Sun Microsystems, see the <a class="external" href="http://www.mozilla.org/projects/ui/accessibility/unix/index.html">Mozilla accessibility on Unix</a> page.</p>
<h3 style="margin-left: 40px;">Creation of IAccessible Objects</h3>
<ul style="margin-left: 40px;"> <p>The first thing that happens when an assistive technology wants to watch our application is that calls the Windows API function AccessibleObjectFromWindow(). This usually happens right after a window gets focused.</p> <p>When the WIN32 API function AccessibleObjectFromWindow() is called, Windows sends the window in question a <a class="external" href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a> message requesting an IAccessible for your root object in the window. In our case, this event is received in <a class="external" href="http://lxr.mozilla.org/seamonkey/source/widget/src/windows/nsWindow.cpp#4370">mozilla/widget/src/windows/nsWindow.cpp</a>. We send back an IAccessible pointer which can be used by the client to get information about this root object. The assistive technology will use that root IAccessible to traverse the rest of the object tree, by navigating to children and then siblings, etc. Every navigation function such as accNavigate(), get_accChild() and get_accParent() returns an IAccessible pointer.</p> <p>To create the root IAccessible for a window the first time it gets the <a class="external" href="http://lxr.mozilla.org/seamonkey/search?string=WM_GETOBJECT">WM_GETOBJECT</a> message in, nsWindow.cpp first generates an internal event called <a class="external" href="http://lxr.mozilla.org/seamonkey/search?string=NS_GETACCESSIBLE">NS_GETACCESSIBLE</a>, which is handled in <a class="external" href="http://lxr.mozilla.org/seamonkey/source/layout/html/base/src/nsPresShell.cpp#6345">PresShell::HandleEventInternal()</a> via the creation of an <a class="external" href="http://lxr.mozilla.org/seamonkey/find?string=msaa/nsDocAccessibleWrap">nsDocAccessibleWrap</a> for an inner window or <a class="external" 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, as well as IAccessible, which is specific to Windows/MSAA/COM. The cross-platform nsDocAccessible and nsRootAccessible classes they inherit from are then told to start listening for DOM, page load and scroll events.  These events cause MSAA-specific events, such as EVENT_OBJECT_FOCUS or EVENT_OBJECT_STATECHANGE, to fire on UI and document objects within the applicable window. We'll explain more about events later in this section.</p> <p>Until the WM_GETOBJECT message is processed, the Gecko accessibility service is not used, and thus the accessibility.dll is not loaded, so there is almost zero overhead for accessibility API support in Mozilla or Gecko, in the general case. Once the accessibility service is created, however, Gecko loads code to create an object on demand for every UI or document object that should support IAccessible. The created objects are cached in a hash table, and shutdown when they're no longer needed. They may still exist in memory in a nonfunctional state until the assistive technology completely releases them. See the section on accessible roles to see what kinds of objects Gecko support IAccessible for.</p>
</ul>
<h3 style="margin-left: 40px;">The Accessible Tree vs. the DOM Tree</h3>
<ul style="margin-left: 40px;"> <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 "give me all 20 children of this object into this array". That's much faster than 20 separate calls, one for each child.</p> <p>In Mozilla, the client has another choice for tree navigation -- it can utilize data stored in the DOM via Mozilla's custom <a class="external" href="http://lxr.mozilla.org/seamonkey/source/accessible/public/msaa/ISimpleDOMNode.idl">ISimpleDOMNode</a> COM interface. Any 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. See the following diagram for examples of nodes that do no support IAccessible.</p>
</ul>
<h3 style="margin-left: 40px;">MSAA tree vs. DOM tree - what's the relationship?</h3>
<ul style="margin-left: 40px;"> <p><img alt="Diagram showing MSAA tree is a subset of the DOM tree" src="http://www.mozilla.org/projects/ui/accessibility/images/tree-relativity.gif" 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,  pAccessible-&gt;<code>QueryInterface(IID_IAccessible)</code> will return null.</ul> <h3 style="margin-left: 40px;">A Variety of Implementations for IAccessible</h3> <div style="margin-left: 40px;"> <div style="margin-left: 40px;"> <p>There are two main kinds of classes in Mozilla's accessibility class hierarchy, platform-specifc and cross-platform. All of the platform-specific classes have the word "Wrap" appended to them. The Wrap classes contain implementations and interfaces specific to MSAA or ATK. These platform-specific classes inherit from cross-platform classes, where the most of the implementation is done. For example, nsAccessibleWrap inherits from nsAccessible. Every accessible object in the MSAA tree has an implementation dertived from nsAccessible, which exposes accessibility information through nsIAccessible, in a generic cross-platform manner.</p> <p>This default implementation for nsIAccessible knows how to use nsAccessibleTreeWalker to walk Mozilla's content DOM and frame tree, exposing only the objects that are needed for accessibility. The nsAccessibleTreeWalker class knows what it needs to expose by asking each DOM node's primary frame (a Gecko formatting object) for an nsIAccessible, using the nsIFrame::GetAccessible() method. If nsAccessibleTreeWalker gets an nsIAccessible back, then the DOM node considered to be an accessible object. The nsIAccessible that is returned is either a new one, or reused from the accessibility cache, and the correct type of accessibility object to correctly expose that DOM node through the cross-platform nsIAccessible and MSAA-specific IAccessible interfaces.</p> <p>Every accessibility object created must be cached, and must inherit from nsAccessibleWrap so that it supports a base implementation of nsIAccessible and IAccessible. Apart from that, it is free to override IAccessible or nsIAccessible methods. In this way each class is tailored to the specific abilities and properties of the HTML or XUL/UI objects it applies to, and can support both MSAA, ATK and hopefully any future accessibility API's we need to support. For example nsHTMLButtonAccessible overrides nsIAccessible::GetAccRole to expose ROLE_BUTTON for IAccessible::get_accRole which uses that.</p> </div> </div> <ul style="margin-left: 40px;"> <p>A more complicated set of nsIAccessible methods which can be overridden are GetAccFirstChild/GetAccLastChild/GetAccChildCount, which allows for objects to define their own decendant 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.</p> </ul> <h3 style="margin-left: 40px;">Generating MSAA Events</h3> <ul style="margin-left: 40px;"> <p>First, keep in mind that most MSAA events aren't utilized by accessibility aids. Therefore we implement only the handful that matter. See the <a class="external" href="file:///c%7C/moz/mozdocs/mozilla-org/html/projects/ui/accessibility/accessible-architecture.html#events">Events</a> cheat sheet above for the list of events we implement. By far the most important one is EVENT_OBJECT_FOCUS.</p> <p>When a potential accessibility-related event occurs within Mozilla, it is typically listened for by nsDocAccessible or nsRootAccessible. The event listeners on these classes call FireToolkitEvent(), which is implemented for every accessible. Eventually, the event ends up at nsDocAccessibleWrap::FireToolkitEvent() which calls NotifyWinEvent from the Win32 API. NotifyWinEvent is passed arguments for the window the event occurred in, and the ID of the child within that window. Accessibility aids use the Win32 call SetWinEventHook() to register as a listener for these events. Creating a unique child ID for every object within a window can be difficult, see the problem and solution for <a class="external" href="file:///c%7C/moz/mozdocs/mozilla-org/html/projects/ui/accessibility/accessible-architecture.html#Hacky_caret_tracking_not_working">no unique child ID for object in window</a>.</p> <p>The assistive technology chooses which events it is interested in learning more about by calling the Win32 method AccessibleObjectFromEvent, which returns the IAccessible to the node corresponding to the child number that had been indicated from NotifyWinEvent(). This ends up asking nsDocAccessibleWrap::get_accChild() for a child IAccessible which matches the child ID we indicated through NotifyWinEvent().</p> <p>In Mozilla, we use the DOM node pointer in the accessible object as a basis for its child ID, which is also used as a hash key into our cache. We also negate the 32 bit value so that it is always &lt;0, telling us that they're really looking for the IAccessible for an event, not child number x. During the callback, we look up the original accessible node in the nsDocAccessible's cache and return it.</p> </ul>
Zu dieser Version zurücksetzen