Visit Mozilla.org

ARIA User Agent Implementors Guide

From MDC

Contents

[edit] 11.3 User Agent Implementation Considerations

If a a user agent already exposes static content via the accessibility API on a given platform, most of the remaining work to enable ARIA can be divided into three parts:

  1. Making focus and tab navigation work on elements that previously were not focusable
  2. Mapping the ARIA roles and properties into the roles, states and other property getters in the accessibility API on a given platform
  3. Computing managed states and events, which are calculated from a mix of internal information

Accessibility APIs covered by this document are:

  • ATK/AT-SPI on Linux
  • MSAA/IAccessible2 on Windows

Information on how to best expose ARIA to Universal Access, UI Automation or other accessibility APIs is not yet available, but would be appreciated, as would any other feedback on the information contained in this document. If you need to expose to other accessibility APIs, it is recommended closely with assistive technology developers.

Note: in the following sections, boolean states from IAccessible2 and ATK are marked in all-caps, e.g. FOCUSABLE.

[edit] 11.3.1 Supporting Keyboard Navigation

Enabling keyboard navigation in web applications is the first step toward making accessible web applications possible. There are two mechanisms that allow for accessible Javascript widgets: HTML5-enhanced tabindex and aria-activedescendant. Most other aspects of ARIA widget development depend on keyboard navigation functioning properly.

Assistive technologies often need to set the focus. For example, voice input software, onscreen keyboards and screen readers supply their own structured navigation modes, providing additional commands for moving to elements in a page. Both the user agent and web pages need to handle this gracefully. JavaScript container widgets cannot expect that focus changes occur only from the keyboard or a mouse click. They need to listen for focus or aria-activedescendant changes and update their state when that occurs.

[edit] 11.3.1.1 HTML 5 Tabindex

HTML 5 expands the usage of tabindex, focus() and blur() to allow them on all HTML elements, to match with current practice in most desktop browsers.

Essentially, any element such as a div, span or img can be added to the default tab order by setting tabindex="0". In addition, any item with tabindex="-1" can be focused via script or a mouse click, but isn't part of the default tab order.

The tabindex system provides one way to develop custom widgets which are keyboard accessible, from simple widgets like a slider to container widgets like a menubar, tree view or spreadsheet. This system works in older versions of Internet Explorer, as far back as IE 5.5.

The user agent implementation must to do the following to enable tabindex on all elements, and to match the new capabilities outlined in HTML 5:

  1. Every element can be a) not focusable, b) focusable but not tabbable, or c) both focusable and tabbable. The presence and value of the tabindex attribute must affect this, even on traditionally non-focusable elements. Mouse click handling and tab navigation code in the user agent must utilize this information.
  2. Every HTMLElement must expose the element.tabIndex property.
  3. The focus() and blur() methods should be added to the HTMLElement interface (available to script for every type of element).
  4. Any element that can receive focus must fire focus and blur events.
  5. The default style sheet must include rules so that any element with :focus receives a focus outline.
  6. Canceling a keydown event must also cancel the keypress event, for purposes of compatibility with other browsers. This is necessary because authors supporting Internet Explorer must use keydown events to process keystrokes, where keydown but not keypress events are fired for non-alphanumeric keys. As web page authors implement JavaScript control over the keyboard they need to be able to use keydown but cancel the effect of consumed keystrokes such as arrow keys.
  7. Ensure that all elements which are focusable are exposed in the accessible object hierarchy, so that when they get focused there is an object to fire an accessibility event for.
  8. Expose the FOCUSED and FOCUSABLE accessibility API states, and focus accessibility API event for any element in the accessibility hierarchy, where appropriate.
  9. XXX should Enter on anything with an onclick trigger act like a click (presumably unless the Enter key is consumed by something else)?

The HTML 5 documentation on what the behavior should be is here: http://www.w3.org/html/wg/html5/#tabindex The Gecko documentation on what the behavior should be is here: http://www.mozilla.org/access/keyboard/tabindex The MSDN docs for IE's tabindex behavior is here: http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/tabindex.asp Opera has some simple testcases: http://tc.labs.opera.com/html/global-attributes/tabindex/

[edit] 11.3.1.2 aria-activedescendant

The aria-activedescendant property can also enable keyboard accessibility on any type of ARIA element. It is often a more convenient way of creating container widget keyboard navigation (where the entire widget is in the tab order just once, but the user can use keystrokes to navigate to descendant items of the container).

Typical usage is described as follows: the author will use tabindex="0" on the container element and aria-activedescendant to point to the ID of the currently active descendant. The author, not the user agent, is responsible for styling the currently active descendant to show it has keyboard focus (not via :focus since actual focus is on the container).

The user agent must do the following to implement aria-activedescendant:

  1. Implement the tabindex recommendations from 11.3.1.1. Support for tabindex="0" on all elements is still necessary for authors to use aria-activedescendant system, because the container widget must be in the tab order.
  2. For an element that has focus in the DOM, but also has aria-activedescendant which points to a valid ID, do not mark it has FOCUSED or FOCUSABLE
  3. When the aria-activedescendant attribute on an element changes, fire an accessibility API FOCUS event on the new active descendant. If aria-activedescendant is cleared or doesn't point to an element in the current document, then fire a focus event for the object that had the attribute change.
  4. For any element with an ID attribute, where the element is a descendant of an element with the aria-activedescendant attribute, ensure an accessible object exists which exposes the following states:
    1. FOCUSABLE, if the element also has an ARIA role -- because the aria-activedescendant of the container can potentially point to it. As a per win, it is not absolutely necessary to check this when there is no role, because real HTML elements that would be focused would already be focusable.
    2. ACTIVE, whenever the container element sets aria-activedescendant to match the ID of this descendant
    3. FOCUSED, if ACTIVE and the container widget with aria-activedescendant has true DOM focus
  5. If the AT sets focus to a descendant of a container with aria-activedescendant, the implementation should change the value of aria-activedescendant to point to that item.

Note for authors: as you move focus to the next descendant, ensure that it is scrolled into view using scrollIntoView() from HTML 5 (works in browsers already).

[edit] 11.3.1.3 Handling focus changes from the AT

Alternative input software needs to be able to set focus to items exposed as FOCUSABLE. Screen readers, voice input software and on screen keyboards may set focus based on user commands implemented in that software. An MSAA-based AT does this via accSelect(SELFLAG_TAKEFOCUS) and in ATK this comes via AtkComponent::grab_focus.

  • If the current element can take real DOM focus, set the DOM focus to it.
  • Otherwise, if the current element has and ID an ancestor with a the aria-activedescendant attribute present, then set DOM focus to that ancestor. If successful, then set aria-activedescendant on the ancestor to the ID of the desired element.

Note for authors: you will need an onfocus handler to enable alternative input. For example, when implementing a listbox with focusable list items, don't assume that a mouse click or key press are the only ways a new list item may receive focus. Therefore, any direct style changes to the focused element should happen in the onfocus handler instead of the click and keypress handlers. For the same reason, when using aria-activedescendant, watch for DOMAttrModifed (onpropertychange in IE) to react to changes to these attributes.

[edit] 11.3.3 Accessible tree vs. DOM tree - what's the relation?

The accessible tree and the DOM tree are parallel structures. Roughly speaking the accessible tree is a subset of the DOM tree. Accessible objects are created in the accessible tree for every DOM element that should be exposed to an assistive technology, either because it may fire an accessible event or because it has a property, relationship or feature which needs to be exposed. Generally if something can be trimmed out it will be, for reasons of performance and simplicity. For example, a <span> with just a style change and no semantics will not get its own accessible object, but the style change will be exposed through a "text attribute run" in the AccessibleText interface.

In addition to the normal accessible tree, ARIA requires that the elements also be exposed if they have one of the following properties:

  • Is focusable, or has an ID attribute and an ancestor with the aria-activedescendant attribute. In either case it may become focused and need to fire a focus event.
  • Uses a role attribute but does not contain the role "presentation" before any other mappable role (see role mapping below in 11.3.5). If the "presentation" role is used the element should still be exposed if it is focusable, so that focus events can be fired (focus must never be lost).
  • Uses one of the global states and properties (but do not use aria-hidden in this list, since hidden elements are typically removed from the accessible object hierarchy anyway)
  • Has an ID which is pointed to by another element via an ARIA relation (aria-controls, aria-describedby, aria-flowto, aria-labelledby or aria-owns)

Objects that should be removed from the accessible hierarchy:

  • Elements with role="presentation" before any other mappable role, and the element is not focusable
  • Table cells where the ancestor <table> has role="presentation", and the table cell is not focusable
  • Objects with a role characteristic "Children Presentational: True". For example, this includes the following roles: "menuitem", "option", "textbox", "button", "image", "slider", "progressbar" and "separator". When descendant trimming occurs, the Accessible Hypertext interface should also not be exposed for the root object that remains. The descendants are removed because their presence confuses some assistive technologies that do not expect descendants for these roles (e.g. the JAWS virtual buffer would speak the accessible name twice or reveal the slider thumb as an image).

[edit] 11.3.4 How ARIA semantics are exposed

[edit] General rules for exposing

Where possible, ARIA semantics should be exposed through the standard mechanisms of the desktop accessibility API. For example, for ARIA widgets, compare how the widget is exposed in a similar desktop widget. In general most ARIA widget capabilities are exposed through the role, value, boolean states and relations of the accessibility API.

[edit] Conflicts between native markup semantics and ARIA

For the core accessibility API properties of role, name, states, value, etc. there is typically only one of each of them. If there is a conflict, ARIA always wins, because ARIA is essentially an override. In other words, if the native markup says there a link, but the ARIA markup says it is a button, then it should be exposed as a button.

[edit] Pushing beyond accessibility API limits

ARIA also exposes new capabilities that desktop accessibility APIs do not currently cover. ARIA uses object attributes to expose semantics not traditionally found in accessibility APIs. Object attributes are name-value pairs that are loosely specified, and very flexible for exposing things that an accessibility API does not have a specific interface for. For example, the aria-live attribute must be exposed via an object attribute because accessibility APIs have no such property available. Specific rules for exposing object attribute name-value pairs are described throughout this document, and rules for the general cases not covered are in 11.3.10 States and Object Properties.

For accessibility APIs that do not have "object attributes" per se, it is useful to find a similar mechanism or develop a new interface to expose name/value pairs. Under Universal Access all getters are already simply name-value pairs and it is possible to expose new semantics whenever necessary. Keep in mind, this also requires working with the assistive technology developers to gain support for the new semantics.

[edit] 11.3.5 Role

In ARIA, the role attribute may indicate more than one role. The role value is an ordered set of space-separated tokens where each token must be a valid role token. ARIA includes some roles, such as landmarks and advanced widgets, that are not traditionally part of accessibility APIs. In addition, future versions of ARIA may allow for author-defined roles to be used in the role string. In that case, it is expected that a fallback role may be provided after the custom role, in the role string.

Accessibility APIs are less flexible than ARIA when it comes to roles. There is a set of basic known roles that map well from most, but not all, ARIA widget roles. There may be an additional mechanism to provide a custom role and localization for that role.

Typically only one role may be exposed, whereas ARIA allows multiple roles. Additional roles may be fallback roles. This may be a backup in case a custom role is unknown to some implementations, or it may be a landmark role. Because landmarks do not have mappings in traditional accessibility APIs, they may occur anywhere within the role string without affecting how the first widget role is exposed.

The user agent should expose roles so that the standard role mechanism provides the best-fit widget role, yet the entire role string is also available for parsing:

  1. The first role with a known mapping to accessibility APIs should be used when mapping to the accessibility API via the standard role mechanism. Use the role table below and apply any special case rules that are specified.
  2. Roles defined in the ARIA spec as "abstract" should not be mapped via the standard role mechanism.
  3. The entire role string should be exposed in the object attribute "xml-roles". This allows assistive technologies can do their own additional processing of roles.
  4. If no best-fit to the standard enumerated roles is found, let the base markup determine what role to expose (e.g. for <table role="log"> expose it with ROLE_SYSTEM_TABLE/ATK_ROLE_TABLE.
  5. In MSAA, if no-best fit is found and the base markup is generic, the role string may also be exposed as a string (VT_BSTR) via IAccessible::get_accRole()

Dynamic role changes are best handled by destroying and recreating the accessible object, and firing the relevant invalidation events:

  • Under MSAA/IAccessible2, the events are EVENT_OBJECT_HIDE and EVENT_OBJECT_SHOW, with EVENT_OBJECT_REORDER on the parent accessible object
  • Under ATK/AT-SPI, fire children-changed:remove followed by children-changed:add for the new object
ARIA role MSAA role IAccessible2 role (if different from MSAA) ATK role
"alert" ROLE_SYSTEM_ALERT ATK_ROLE_ALERT
"alertdialog" ROLE_SYSTEM_ALERT ATK_ROLE_ALERT
"application" ROLE_SYSTEM_APPLICATION ATK_ROLE_EMBEDDED
"button" ROLE_SYSTEM_PUSHBUTTON. If a "button" has the state aria-haspopup="true" it should be exposed as a ROLE_SYSTEM_BUTTONMENU If a "button" has a non-empty aria-pressed attribute, it should be exposed in IA2 as IA2_ROLE_TOGGLE_BUTTON ATK_ROLE_PUSH_BUTTON
"checkbox" ROLE_SYSTEM_CHECKBUTTON + object attribute checkable="true" ATK_ROLE_CHECK_BOX + object attribute checkable="true"
"columnheader" ROLE_SYSTEM_COLUMNHEADER ATK_ROLE_COLUMN_HEADER
"combobox" ROLE_SYSTEM_COMBOBOX + STATE_SYSTEM_HASPOPUP. If aria-expanded != "true", expose STATE_SYSTEM_COLLAPSED ATK_ROLE_COMBO_BOX + ATK_STATE_EXPANDABLE + object attribute haspopup="true"
"description" No role mapping, use BSTR role IA2_ROLE_TEXT_FRAME ATK_ROLE_TEXT
"dialog" ROLE_SYSTEM_DIALOG ATK_ROLE_DIALOG
"document" ROLE_SYSTEM_DOCUMENT ATK_ROLE_DOCUMENT_FRAME
"grid" ROLE_SYSTEM_TABLE ATK_ROLE_TABLE
"gridcell" ROLE_SYSTEM_CELL ATK_ROLE_TABLE_CELL
"group" ROLE_SYSTEM_GROUPING ATK_ROLE_PANEL
"heading" None, use BSTR role IA2_ROLE_HEADING ATK_ROLE_HEADING
"img" ROLE_SYSTEM_GRAPHIC ATK_ROLE_IMAGE
"label" ROLE_SYSTEM_STATICTEXT IA2_ROLE_LABEL ATK_ROLE_LABEL
"link" ROLE_SYSTEM_LINK. Also, apply special rule to expose STATE_LINKED to link and all its descendants. ATK_ROLE_LINK
"list" ROLE_SYSTEM_LIST + STATE_SYSTEM_READONLY ATK_ROLE_LIST
"listbox" ROLE_SYSTEM_LIST ATK_ROLE_LIST. Special case: if a "listbox" has a parent or is owned by (via aria-owns) a "combobox", it should be exposed with ATK_ROLE_MENU.
"listitem" ROLE_SYSTEM_LISTITEM + STATE_SYSTEM_READONLY ATK_ROLE_LISTITEM
"menu" ROLE_SYSTEM_MENUPOPUP ATK_ROLE_MENU. These objects should not be exposed for a submenu if there is a parent menu item spawning the submenu (XXX Mozilla todo).
"menubar" ROLE_SYSTEM_MENUBAR ATK_ROLE_MENU_BAR
"menuitem" ROLE_SYSTEM_MENUITEM ATK_ROLE_MENU_ITEM
  • If aria-checked is not empty, support object attribute "checkable"="true"
"menuitemcheckbox" ROLE_SYSTEM_MENUITEM + object attribute checkable=true IA2_ROLE_CHECK_MENU_ITEM + object attribute checkable="true" ATK_ROLE_CHECK_MENU_ITEM + object attribute checkable="true"
"menuitemradio" ROLE_SYSTEM_MENUITEM + object attribute checkable=true IA2_ROLE_RADIO_MENU_ITEM + object attribute checkable="true" ATK_ROLE_RADIO_MENU_ITEM + object attribute checkable="true"
"option" ROLE_SYSTEM_LISTITEM + if aria-checked is not empty, support object attribute "checkable"="true" ATK_ROLE_LIST_ITEM
  • If aria-checked is not empty, support object attribute "checkable"="true"
  • Special case: if an "option" has a parent that was exposed as an ATK_ROLE_MENU, it should be exposed as an ATK_ROLE_MENU_ITEM
"presentation" Do not expose this object unless it is focusable Do not expose this object unless it is focusable
"progressbar" ROLE_SYSTEM_PROGRESSBAR + READONLY ATK_ROLE_PROGRESS_BAR + READONLY
"radio" ROLE_SYSTEM_RADIOBUTTON ATK_ROLE_RADIO_BUTTON
"radiogroup" ROLE_SYSTEM_GROUPING ATK_ROLE_PANE
"region" ROLE_SYSTEM_PANE ATK_ROLE_PANE
"row" ROLE_SYSTEM_ROW unless inside a "tree" or "treegrid", in which case ROLE_SYSTEM_OUTLINEITEM ATK_ROLE_LIST_ITEM
"rowheader" ROLE_SYSTEM_ROWHEADER ATK_ROLE_ROW_HEADER
"section" None, use BSTR role IA2_ROLE_SECTION ATK_ROLE_SECTION
"separator" ROLE_SYSTEM_SEPARATOR ATK_ROLE_SEPARATOR
"slider" ROLE_SYSTEM_SLIDER ATK_ROLE_SLIDER
"spinbutton" ROLE_SYSTEM_SPINBUTTON ATK_ROLE_SPIN_BUTTON
"status" ROLE_SYSTEM_STATUSBAR ATK_ROLE_STATUSBAR
"tab" ROLE_SYSTEM_PAGETAB. Expose SELECTED state if focus is inside tabpanel associated with aria-labelledby. ATK_ROLE_PAGE_TAB. Expose SELECTED state if focus is inside tabpanel associated with aria-labelledby.
"tablist" ROLE_SYSTEM_PAGETABLIST ATK_ROLE_PAGE_TAB_LIST
"tabpanel" ROLE_SYSTEM_PROPERTYPAGE ATK_ROLE_SCROLL_PANE
"textbox" ROLE_SYSTEM_TEXT + IA2_STATE_SINGLE_LINE of aria-multiline is not "true" ATK_ROLE_ENTRY + ATK_STATE_SINGLE_LINE of aria-multiline is not "true"
"toolbar" ROLE_SYSTEM_TOOLBAR ATK_ROLE_TOOL_BAR
"tooltip" ROLE_SYSTEM_TOOLTIP ATK_ROLE_TOOL_TIP
"tree" ROLE_SYSTEM_OUTLINE ATK_ROLE_TREE
"treegrid" ROLE_SYSTEM_OUTLINE ATK_ROLE_TREE_TABLE
"treeitem" ROLE_SYSTEM_OUTLINEITEM + if aria-checked is not empty, support object attribute "checkable"="true" ATK_ROLE_LIST_ITEM + if aria-checked is not empty, support object attribute "checkable"="true"

[edit] 11.3.6 Name

To compute the accessible name:

  1. If aria-labelledby is present, collect the name from the content subtrees pointed to by aria-labelledby. Process the IDs in aria-labelledby in the order they occur. Ignore IDs that are not specified on an element in the document. For each ID use a depth-first computation of the name, appending to the currently computed name. See the definition of depth-first name computation below.
  2. If aria-labelledby is not present, and there is a role mapping to the accessibility APIs (see 11.3.5), for the ARIA role that was mapped, check the specification to see that role allows "Name From: subtree". If the name can be computed from the subtree, use a depth-first computation of the name. The specification generally allows subtree computation for descendant widgets of container widgets, but not the container widgets themselves. [I don't understand what the previous sentence means. Does "generally" mean that there are exceptions? In which case, what are they?] See the definition of depth-first name computation below.
  3. If the computed name is still empty, use the standard name computation for that element. [What's the standard name computation? Pointer?] For HTML <img> the "alt" attribute should be preferred. [preferred over what?] For the root accessible in a document, the <title> element should be used. [any reason the previous shoulds can't be musts?]
  4. If the computed name is still empty, fall back on the "title" or other equivalent attribute for the given element type. [which are the equivalent attributes for different element types? what happens when there are multiple attributes that are "equivalent"? -simonp]

Definition of depth first name computation (used in rule 1 and 2) : Iterate through the elements of the given content subtree in depth first order. For each element,

  1. If a text node, append the text contents
  2. If a control, append the current value text for the control
  3. If an image or object with a text equivalent, append the text equivalent
  4. If there is a forced line break (e.g if the current object is styled with display:block), append a space character

[edit] 11.3.7 Value

When the aria-valuenow attribute is supported for a role used on the element, the AccessibleValue interface must be supported on the accessible object to expose. The aria-valuemin and aria-valuemax should be exposed via the AccessibleValue interface.

There may also be a text equivalent for the numerical value, which is set via the aria-valuetext property. That should be exposed via a "valuetext" object attribute, or a similar mechanism if object attributes are not available in the API. When no aria-valuetext is present, then expose a string version of the aria-valuenow in the valuetext object attribute.

In addition, for MSAA, the string should also be exposed via IAccessible::get_accValue().

Changes to aria-valuenow should be exposed via value change events.

If the value is not set on a control that requires value, then current value should return an error. This is a valid condition for progressbar, where the current value could be indeterminate.

The AcessibleValue allows values to be set. If the object is not readonly or disabled, it should allow this and the UA should set aria-valuenow to reflect the new value. It is not possible to alter the value in a progress meter since it is always readonly. The implementation should reject values which are less than the valuemin or greater than the valuemax. If the value cannot be set because the object is readonly or disabled, the new value would be out of bounds, the implementation should throw an exception rather than set the value. Note to authors: a JavaScript widget which supports aria-valuenow and is not readonly or disabled should listen for aria-valuenow changes and when it changes, reset the internal state, the aria-valuetext (if used) and the visual state of the widget.

[edit] 11.3.8 Relations

All relations are globally applicable to any element, so it is not important to check the role before computing them. Relationship attributes use an ID list (space separated list of IDs). A relationship ID matches the element that is returned by getElementById with the ID as an argument.

Exposing forward relations:

  • aria-controls maps to CONTROLLER_FOR
  • aria-describedby maps to DESCRIBED_BY
  • aria-flowto maps to FLOW_TO
  • aria-labelledby maps to LABELLED_BY
  • aria-owns has no standard API relation mapping in ATK/AT-SPI or IAccessible2

Computing reverse relations:

  • Check for other elements using ARIA relations that point to the current element's ID, in order to expose the reverse relations
  • If aria-controls points to the element: expose RELATION_CONTROLLED_BY
  • If aria-describedby points to the element: expose RELATION_DESCRIPTION_FOR
  • If aria-flowto points to the element: expose RELATION_FLOW_FROM
  • If aria-labelledby points to the element: expose RELATION_LABEL_FOR
  • If aria-owns points to the element: expose RELATION_NODE_CHILD_OF

If both aria-labelledby and HTML <label for=> are used, the ARIA relation wins and the HTML label relation is ignored.

Computing RELATION_NODE_CHILD_OF for role="treeitem", when aria-owns is not used:

  • If the current treeitem uses aria-level, then walk backwards in the tree until a treeitem is found with a lower aria-level, and point to that. If the top of the tree is reached, then point to the tree itself.
  • If the parent of the treeitem is role="group", then walk backwards from the group until a treeitem is found and point to that. This is the case where role="group" is used to organize levels in the tree.

Computing RELATION_MEMBER_OF from aria-atomic:

  • Check the chain of ancestor elements for aria-atomic="true". All accessible descendants of that element should use the RELATION_MEMBER_OF relation to point to the ancestor that sets aria-atomic="true".

[edit] 11.3.9 Group Position

The object attributes "posinset", "setsize" and "level" should be exposed when the equivalent ARIA properties are supported by a role used on the element. In addition, on IAccessible2 the same information must be exposed via IAccessible2::groupPosition().

Computing the level if not provided:

  • For role="treeitem", if aria-level is not provided by the author, it must be computed by following the explicit or computed RELATION_NODE_CHILD_OF as described in 11.3.8.

Computing posinset and setsize if not provided:

  • For role="treeitem", walk the tree backward and forward until the explicit or computed level becomes less than the original items level. Count items only if they are at the same level as the current item.
  • Otherwise, walk backward and forward within the DOM parent, counting items that have the same role.

These properties are all 1-based. When the object property is not present or holds a value of "0", it indicates the property is not computed or not supported.

Because these values are 1-based, the current item must be included in the computation. For posinset, add items only if they are before the current item in the DOM. For setsize, also add items after the current item in the DOM.

[edit] 11.3.10 States and Object Properties

This section describes how to expose additional states and object properties not covered in previous sections of this document. Where possible, API standard states are used. When that is not possible, object attributes (or a similar mechanism) is required.

  1. Compute the managed states such as VISIBLE/INVISIBLE, SHOWING/OFFSCREEN, etc. This typically is done in the same way as for ordinary elements that do not have ARIA attributes present. For FOCUSABLE/FOCUSED, it may be affected by aria-activedescendant -- see the rules in 11.3.1.2.
  2. Add in states and object attributes supported by the accessibility implementation for the base markup.
  3. Apply special case rules for the primary role: some roles require additional states to be exposed. For the primary mapped role, look in the role table in 11.3.5 for additional states and object attributes to expose.
  4. Compute states for the relevant ARIA properties. To determine the relevant ARIA properties, combine the list of the global states and properties with the specific states and properties supported by the roles on the current element. For example, all elements support aria-required, but only some roles such as role="checkbox" support the aria-checked property. To compute the properties see the table below.
  5. For both performance and forward compatibility with new ARIA properties in future versions, it may be desirable to implement a general case rule for mapping some attributes. This is done by keeping a blacklist of ARIA attributes where specific rules exist to map them. For any attribute with "aria-" in the prefix, that is not in the blacklist, expose the value in an object attribute with an attribute name that just removes the "aria-". For example, aria-foo="bar" would be exposed with an object attribute foo=bar, since aria-foo is not in the blacklist (it is not already covered by a more specific mapping rule).

ARIA property mapping rule table:

ARIA state What to expose
aria-activedescendant Expose as described in 11.3.1.2, and set appropriately when the AT pushes focus changes in a container with this attribute not empty as described in 11.3.1.3.
aria-atomic If "true", expose atomic="true". In addition, expose container-atomic="true" on all descendants as well as RELATION_MEMBER_OF pointing to this element (the atomic root).
aria-autocomplete
  • if not empty or "none" expose the value in an object attribute called autocomplete and expose the SUPPORTS_AUTOCOMPLETION equivalent state
  • In MSAA, if "list" or "both" expose STATE_SYSTEM_HASPOPUP
  • In ATK, if "list" or "both" expose an object attribute haspopup="true"
aria-busy
  • If "true", expose as BUSY equivalent state
  • If "error", expose as INVALID_ENTRY
aria-channel If "notify", expose channel="notify" and container-channel="notify" on all descendants
aria-checked
  • If aria-checked="true", Expose via the CHECKED equivalent state
  • If aria-checked is "mixed" expose ATK_STATE_INDETERMINATE/STATE_SYSTEM_MIXED
  • In addition, if aria-checked is not empty expose object attribute checkable="true"
aria-controls Expose the relations as described in 11.3.8
aria-datatype Expose as object attribute "datatype"
aria-describedby Expose the relations as described in 11.3.8
aria-disabled
  • In MSAA, if aria-disabled="true", expose STATE_SYSTEM_UNAVAILABLE
  • In all APIs, if aria-disabled != "true", expose SENSITIVE + ENABLED
  • In addition, aria-disabled="true" propagates to all FOCUSABLE descendants of the element with aria-disabled set
aria-dropeffect Expose as object attribute "dropeffect"
aria-expanded
  • In MSAA, if "true" expose as STATE_SYSTEM_EXPANDED, and if "false" (not empty) expose as STATE_SYSTEM_COLLAPSED
  • in ATK, if not empty expose ATK_STATE_EXPANDABLE. In addtion, if "true" expose ATK_STATE_EXPANDED
aria-flowto Expose the relations as described in 11.3.8
aria-invalid If "true", expose the INVALID_ENTRY equivalent state
aria-haspopup

If "true":

  • In MSAA, expose as STATE_SYSTEM_HASPOPUP. If on a button, change the role to ROLE_SYSTEM_BUTTONMENU
  • In ATK, expose as object attribute haspopup="true"
aria-labelledby Expose the relations as described in 11.3.8
aria-level Expose value in"level" object attribute and in IAccessible2's groupPosition(). May affect RELATION_NODE_CHILD_OF when used on a tree item. See section 11.3.9.
aria-live If not "off", expose the value in an object attribute called "live". Expose a "container-live" object attribute with the value on all descendants.
aria-multiline If "true", expose the MULTI_LINE state for IA2/ATK
aria-multiselectable Expose MULTISELECTABLE state. In MSAA also expose STATE_SYSTEM_EXTSELECTABLE. Expose the AccessibleSelection interface. See selection section 11.3.14.
aria-owns Expose the relations as described in 11.3.8
aria-pressed
  • If aria-pressed="true", Expose via the PRESSED equivalent state. If on a button, change the role to IA2_ROLE_TOGGLE_BUTTON/ATK_ROLE_TOGGLE_BUTTON.
  • If aria-pressed is "mixed" expose ATK_STATE_INDETERMINATE/STATE_SYSTEM_MIXED
  • In addition, if aria-pressed is not empty expose object attribute checkable="true"
aria-posinset Expose value in "posinset" object attribute and in IAccessible2's groupPosition()
aria-readonly
  • In MSAA, expose as STATE_SYSTEM_READONLY
  • In ATK, ensure ATK_STATE_EDITABLE is not set
aria-relevant If not empty, expose the value in an object attribute called "relevant". Expose a "container-relevant" object attribute with the value on all descendants.
aria-required Expose as REQUIRED state
aria-selected
  • If aria-selected="true" expose the SELECTED equivalent state
  • If not empty, also expose SELECTABLE
  • If SELECTABLE and not DISABLED, toggle between "true" and "false" as appropriate when the Accessible Selection interface is used on an ancestor with aria-multiselectable.
aria-setsize Expose value in "setsize" object attribute and in IAccessible2's groupPosition()
aria-sort Expose value in "sort" object attribute
aria-templateid XXX not sure what to do with this one
aria-valuemax Expose via AccessibleValue interface as described in 11.3.7
aria-valuemin Expose via AccessibleValue interface as described in 11.3.7
aria-valuenow Expose via AccessibleValue interface as described in 11.3.7, and set when the AT uses the Value interface to set the value on an element that is not DISABLED or READONLY, and a valid value is chosen. In MSAA, also expose via get_accValue() unless aria-valuetext is used to override the value's text equivalent.
aria-valuetext Expose in "valuetext" object attribute as described in 11.3.7

For dynamic ARIA property changes, expose an event such as a state change event to indicate the change occured. For simplicity and performance the user agent may trim out change events for properties that assistive technologies typically ignore changes in. However, at a miminum, state change events should be fired for changes in:

  • aria-disabled: this will affect ENABLED and SENSITIVE states
  • aria-checked or aria-pressed: this will affect the CHECKED state. A state change for MIXED/INDETERMINATE should also be fired when the value changes to or from "mixed".
  • aria-invalid: this will affect the INVALID_ENTRY state (be careful, the INVALID state in APIs means no valid state, which is different)
  • aria-expanded, aria-readonly, aria-required: these will affect the states of similar names in the accessibility APIs

Other types of changes:

  • aria-activedescendant changes affect focus events as described in 11.3.1.2
  • aria-valuenow changes affect value change events as described in 11.3.7
  • role or aria-multiselectable changes should cause the destruction and recreation of a new accessible object for the element, because it is a major change where the interfaces exposed via acessibility APIs change as well.
  • aria-selected changes should affect selection events as described in 11.3.14

[edit] 11.3.11 Exposing Supplemental Interfaces

In general the base markup should determine what interfaces are exposed for an accessible object. However, in the following cases ARIA markup changes which interfaces should be exposed:

  • AccessibleValue: as discussed in 11.3.7, the value interface should be exposed for objects with an aria-valuenow property when one of the roles on the elements supports that. Because the Value interface allows values to be set, aria-valuenow is really read/write. The browser needs to set aria-valuenow if the value is changed to a valid value, and the current element is not readonly or disabled. Note to authors: anything that implements valuenow needs to watch for on DOMAttrChanged (onpropertychange in IE) and react to it.
  • AccessibleTable: should be exposed for role="grid" and role="treegrid" (XXX note: not done in Mozilla -- known bug)
  • AccessibleSelection: should be exposed when a role supports aria-multiselectable, and aria-multiselectable="true"
  • AccessibleHypertext: this should not be exposed if the elements descendants have been trimmed off based on the role. See 11.3.2.1 for which roles have their subtrees trimmed.

Although it is not an ARIA-specific issue, for the purposes of accessible web applications it is worth mentioning some additionally useful rules for exposing interfaces:

  • AccessibleAction: should at least be exposed for anything with a "click" handler, although future versions of ARIA may bring more precision to the handling of actions. Also need to support doDefaultAction in MSAA (same as action #0) -- this maps to a click.
  • AccessibleEditabletext: should be exposed for any region made editable via contenteditable or designMode

[edit] 11.3.12 Changes to document content or node visibility

Processing document changes is important regardless of ARIA. We document how to do it here, however, because it is so crucial to enable the AJAX and other use cases that often go along with ARIA markup.

Fire these events for text changes:

  1. When text is removed, fire IA2_EVENT_TEXT_REMOVED (IA2) and text_changed:delete (ATK)
  2. When text is inserted, IA2_EVENT_TEXT_INSERTED (IA2) and text_changed:insert (ATK)
  3. When text is changed, fire a removal event followed by an insertion event

Fire these events for node changes where the node in question is an element and has an accessible object:

  1. When a subtree is removed or hidden, fire EVENT_OBJECT_HIDE (MSAA) and children_changed:remove (ATK). The MSAA event called EVENT_OBJECT_DESTROY is not used because this has a history of stability issues and ATs avoid it. In any case, from the user's point of view, there is no difference between something that is hidden or destroyed.
  2. When a subtree is inserted or shown, fire EVENT_OBJECT SHOW (MSAA) and children_changed:add (ATK).
  3. When a subtree is moved, treat it as a removal from one place and insertion in another
  4. When a subtree is changed (e.g. replaceNode) treat it as a removal and insertion

For node changes where the node is not an element or has no accessible object:

  • This will likely affect the text output by the AccessibleText interface. Compute and fire relevant text change events as described above.
  • There may be accessible descendants. The appropriate show/hide/children_changed events must be fired for the first-line descendants with accessible objects.

When firing any of the above-mentioned change events, it is very useful to provide information about whether the change was caused by user input (as opposed to a timeout initiated from the page load, etc.). This allows the AT to have different rules for presenting changes from the real world as opposed to from user action. Mouse hovers are not considered explicit user input because they can occur from accidental bumps of the mouse.

To expose whether a change occurred from user input:

  • In ATK this can be provided by appending the string ":system" to the event name when the user did not cause the change.
  • In MSAA, which screen readers typically access in process on the same thread, the best practice is to expose the object attribute "event-from-user-input=true" on the accessible object for the event, if the user caused the change.

Exposing additional useful information about the context of the change:

  • The member-of relation on the accessible event object should point to any ancestor with aria-atomic="true" (if any).
  • The container-live, container-channel, container-relevant, container-busy, container-atomic object attributes should be exposed on the accessible event object, providing the computed value for the related ARIA properties. The computed value is the value of the closest ancestor. It is recommended to not expose the object attribute if the default value is used.

Additional MSAA events may be necessary:

  • If something changes in an ancestor with a mapped MSAA role of ROLE_SYSTEM_ALERT, then an EVENT_SYSTEM_ALERT event should be fired for the alert.
  • Menu events may need to be fired. See 11.3.15.

[edit] 11.3.14 Selection

There are two cases for selection:

  • Single selection
  • Multiple selection

In the single selection case, it is not always necessary to manage selection events and states separate from focus, since selection mirrors focus. One exception is for tab lists. In the case of a tab, if either the tab or its associated tabpanel has focus, then the tab is considered to be SELECTED. To implement this: the user agent can walk up the parent chain from the focus until it finds the a tabpanel, then traverse the aria-labelledby relation from the tabpanel to the related tab, and mark the found tab as focused.

The multiple selection case occurs when aria-multiselectable="true" on an element with a role that supports that property. There are several important aspects:

  1. Expose the correct states on the container: MULTISELECTABLE and, in MSAA, it is also EXTSELECTABLE
  2. Support the AccessibleSelection interface on the container with aria-multiselectable. The selection interface can be used by an AT to actually set the selection on a descendant. This should fail for the specified descendant if aria-selected is empty, which indicates the element is not SELECTABLE. It should also fail if the specified descendant is DISABLED or READONLY for any reason. When clearing selection on an item, set aria-selected="false" but do not remove the attribute, so that it is still SELECTABLE. Note to authors: scripts need to watch for mutations to aria-selected, since selection may be caused by an AT and not the mouse or keyboard.
  3. Fire the correct events when aria-selected changes on a descendant, as follows:
Scenario MSAA/IA2 ATK/AT-SPI
Toggle aria-selected EVENT_OBJECT_SELECTIONADD/EVENT_OBJECT_SELECTIONREMOVE on the current container + EVENT_OBJECT_STATECHANGE on the item object::selection_changed + atk_object_notify_state_change() on the item
Selection follows focus EVENT_OBJECT_SELECTION then state change event on newly focused item, but arrange events so state change doesn't occur on focused item, to avoid extra selection change announcements object:selection_changed + atk_object_notify_state_change(), but arrange events so state change doesn't occur on focused item, to avoid extra selection change announcements
Select or deselect many items at once: EVENT_OBJECT_SELECTIONWITHIN is all that is necessary. The state change events may be trimmed out for performance. object:selection_changed. The state change events may be trimmed out for performance

[edit] 11.3.15 Menus in MSAA/IAccessible2

Under MSAA special events are required whenever a menu is opened or closed. These events must be nested and symmetrical:

  • A single EVENT_SYSTEM_MENUSTART when a menu in a menubar is opened. This is triggered when focus goes to a menuitem and menu mode is not currently active, but it must be fired before any EVENT_SYSTEM_MENUPOPUPSTART events. The menubar must be either in the natural parent chain or or in the one caused by aria-owns (use RELATION_NODE_CHILD_OF impl to go to aria-owns ancestor).
  • One or more EVENT_SYSTEM_MENUPOPUPSTART when a menu popup is made visible. This is triggered when a menu is made visible, but should only be fired once until the menu is closed and reopened again. Care should be taken to check descendants for role="menu" when any subtree is made visible. The menu role might not be on the root node of the change.
  • A focus event on a menuitem. This must occur after the EVENT_SYSTEM_MENUPOPUPSTART.
  • One or more EVENT_SYSTEM_MENUPOPUPEND when a menu popup is hidden. This should only be fired for an accessible menu object when an EVENT_SYSTEM_MENUPOPUPSTART was fired for it, and it should only be fired once in that case. Care should be taken to check descendants for role="menu" when a subtree is hidden.
  • A final EVENT_SYSTEM_MENUEND when in menubar mode (a MENUSTART was fired) and either all menus in a menubar are closed or focus goes somewhere other than a menuitem (such as a control in a dialog).

Because screen readers on Windows will typically ignore focus events while menus are opened, it is important to fire the symmetrical EVENT_SYSTEM_MENUPOPUPEND followed by a EVENT_SYSTEM_MENUEND when something other than a menu received focus (e.g. selecting the menu option opened a dialog). In fact, screen readers can become quite confused if the correct ending menu events are not fired.

That said, perfectly symmetrical events are difficult to achieve. Because menus can be made visible hidden using a variety of techniques, it is advisable that an implementation keep track of the menu events fired and ensure symmetrization.

[edit] 11.3.16 Documents, Handling <frame>s and <iframe>s

[edit] Definitions

  • Root ARIA node: the <body> or <frameset> in HTML, or the document element in all other languages.
  • Sub-document: any document created from a <frame>, <iframe> or similar mechanism. A sub-document may contain a document, an application or any widget such as a calendar pulled in from another server. In the accessibility hierarchy there are two accessible objects for this situation -- one represents the <frame>/<iframe> element in the parent document, which parents a single accessible object child representing the spawned document contents.
  • Outer document accessible: the accessible object representing the <frame>/<iframe>
  • Inner document accessible: the accessible object representing the root ARIA node for the spawned document. It is a child of the outer document accessible.

[edit] Accessible property computation

Computing the accessible name for an outer document accessible:

  • Accessibility properties for the outer document accessible are exposed as they normally are for an element.

Computing the accessible name for an inner document accessible:

  1. If a sub-document, do a depth-first name computation using aria-labelledby from the <frame> or <iframe>. If the name is still empty, use the title attribute from the <frame> or <iframe>.
  2. If the name is still empty, use a depth-first name computation from aria-labelledby on the document's root ARIA node. If it is still empty use the title attribute on the root ARIA node.
  3. If the name is still empty, and the <title> element or some other means exists of getting the accessible name, use that.

Computing the accessible description for an inner document accessible:

  1. If a sub-document, do a depth-first description computation using aria-describedby from the <frame> or <iframe>.
  2. If the description is still empty, use a depth-first name computation from aria-describedby on the document's root ARIA node.

Computing container-foo on any node in a sub document: For container-live, container-atomic, container-relevant, container-channel and container-busy, inner nodes override outer nodes from within the same documment, because the inner subtree is the more relevant context. However, outer documents override inner documents, because the outer document author may be different and may wish to define the context for a live iframe. Therefore:

  1. Walk the entire parent chain including that from outer documents, collecting the properties from aria-live, aria-atomic, aria-relevant, aria-channel and aria-busy into the container-[property] object attribute
  2. If a node sets a given object attribute, set a state that doesn't allow that value to change that object attribute again within the document
  3. When entering a parent document, refresh the state to again allow override of each of these object properties

Computing other properties for an inner document accessible:

  1. For user-controlled properties (aria-selected, aria-valuenow, aria-valuetext, aria-activedescendant), use the ARIA markup on the root ARIA node only
  2. The ARIA properties on the outer document accessible take precedence, on a property-by-property basis
  3. If the outer document accessible does not set a property, the root ARIA node for the inner document is used
  4. Relations are not concatenative. If the outer document accessible sets a relation, that is used instead of anything set on the inner document's root ARIA node.