Index: docs/accessibility.md |
diff --git a/docs/accessibility.md b/docs/accessibility.md |
index c0c57797e44864848ed8e8fb97381b9808025d7c..b12e90894f72949d7132159bb33c7b957c6fc1c7 100644 |
--- a/docs/accessibility.md |
+++ b/docs/accessibility.md |
@@ -1,515 +1,10 @@ |
-# Accessibility Overview |
+# Accessibility |
-Accessibility means ensuring that all users, including users with disabilities, |
-have equal access to software. One piece of this involves basic design |
-principles such as using appropriate font sizes and color contrast, |
-avoiding using color to convey important information, and providing keyboard |
-alternatives for anything that is normally accomplished with a pointing device. |
-However, when you see the word "accessibility" in a directory name in Chromium, |
-that code's purpose is to provide full access to Chromium's UI via external |
-accessibility APIs that are utilized by assistive technology. |
+* [Accessibility Overview](accessibility/overview.md) |
-**Assistive technology** here refers to software or hardware which |
-makes use of these APIs to create an alternative interface for the user to |
-accommodate some specific needs, for example: |
+## Chrome OS |
-Assistive technology includes: |
- |
-* Screen readers for blind users that describe the screen using |
- synthesized speech or braille |
-* Voice control applications that let you speak to the computer, |
-* Switch access that lets you control the computer with a small number |
- of physical switches, |
-* Magnifiers that magnify a portion of the screen, and often highlight the |
- cursor and caret for easier viewing, and |
-* Assistive learning and literacy software that helps users who have a hard |
- time reading print, by highlighting and/or speaking selected text |
- |
-In addition, because accessibility APIs provide a convenient and universal |
-way to explore and control applications, they're often used for automated |
-testing scripts, and UI automation software like password managers. |
- |
-Web browsers play an important role in this ecosystem because they need |
-to not only provide access to their own UI, but also provide access to |
-all of the content of the web. |
- |
-Each operating system has its own native accessibility API. While the |
-core APIs tend to be well-documented, it's unfortunately common for |
-screen readers in particular to depend on additional undocumented or |
-vendor-specific APIs in order to fully function, especially with web |
-browsers, because the standard APIs are insufficient to handle the |
-complexity of the web. |
- |
-Chromium needs to support all of these operating system and |
-vendor-specific accessibility APIs in order to be usable with the full |
-ecosystem of assistive technology on all platforms. Just like Chromium |
-sometimes mimics the quirks and bugs of older browsers, Chromium often |
-needs to mimic the quirks and bugs of other browsers' implementation |
-of accessibility APIs, too. |
- |
-## Concepts |
- |
-While each operating system and vendor accessibility API is different, |
-there are some concepts all of them share. |
- |
-1. The *tree*, which models the entire interface as a tree of objects, exposed |
- to assistive technology via accessibility APIs; |
-2. *Events*, which let assistive technology know that a part of the tree has |
- changed somehow; |
-3. *Actions*, which come from assistive technology and ask the interface to |
- change. |
- |
-Consider the following small HTML file: |
- |
-``` |
-<html> |
-<head> |
- <title>How old are you?</title> |
-</head> |
-<body> |
- <label for="age">Age</label> |
- <input id="age" type="number" name="age" value="42"> |
- <div> |
- <button>Back</button> |
- <button>Next</button> |
- </div> |
-</body> |
-</html> |
-``` |
- |
-### The Accessibility Tree and Accessibility Attributes |
- |
-Internally, Chromium represents the accessibility tree for that web page |
-using a data structure something like this: |
- |
-``` |
-id=1 role=WebArea name="How old are you?" |
- id=2 role=Label name="Age" |
- id=3 role=TextField labelledByIds=[2] value="42" |
- id=4 role=Group |
- id=5 role=Button name="Back" |
- id=6 role=Button name="Next" |
-``` |
- |
-Note that the tree structure closely resembles the structure of the |
-HTML elements, but slightly simplified. Each node in the accessibility |
-tree has an ID and a role. Many have a name. The text field has a value, |
-and instead of a name it has labelledByIds, which indicates that its |
-accessible name comes from another node in the tree, the label node |
-with id=2. |
- |
-On a particular platform, each node in the accessibility tree is implemented |
-by an object that conforms to a particular protocol. |
- |
-On Windows, the root node implements the IAccessible protocol and |
-if you call IAccessible::get_accRole, it returns ROLE_SYSTEM_DOCUMENT, |
-and if you call IAccessible::get_accName, it returns "How old are you?". |
-Other methods let you walk the tree. |
- |
-On macOS, the root node implements the NSAccessibility protocol and |
-if you call [NSAccessibility accessibilityRole], it returns @"AXWebArea", |
-and if you call [NSAccessibility accessibilityLabel], it returns |
-"How old are you?". |
- |
-The Linux accessibility API, ATK, is more similar to the Windows APIs; |
-they were developed together. (Chrome's support for desktop Linux |
-accessibility is unfinished.) |
- |
-The Android accessibility API is of course based on Java. The main |
-data structure is AccessibilityNodeInfo. It doesn't have a role, but |
-if you call AccessibilityNodeInfo.getClassName() on the root node |
-it returns "android.webkit.WebView", and if you call |
-AccessibilityNodeInfo.getContentDescription() it returns "How old are you?". |
- |
-On Chrome OS, we use our own accessibility API that closely maps to |
-Chrome's internal accessibility API. |
- |
-So while the details of the interface vary, the underlying concepts are |
-similar. Both IAccessible and NSAccessibility have a concept of a role, |
-but IAccessible uses a role of "document" for a web page, while NSAccessibility |
-uses a role of "web area". Both IAccessible and NSAccessibility have a |
-concept of the primary accessible text for a node, but IAccessible calls |
-it the "name" while NSAccessibility calls it the "label", and Android |
-calls it a "content description". |
- |
-**Historical note:** The internal names of roles and attributes in |
-Chrome often tend to most closely match the macOS accessibility API |
-because Chromium was originally based on WebKit, where most of the |
-accessibility code was written by Apple. Over time we're slowly |
-migrating internal names to match what those roles and attributes are |
-called in web accessibility standards, like ARIA. |
- |
-### Accessibility Events |
- |
-In Chromium's internal terminology, an Accessibility Event always represents |
-communication from the app to the assistive technology, indicating that the |
-accessibility tree changed in some way. |
- |
-As an example, if the user were to press the Tab key and the text |
-field from the example above became focused, Chromium would fire a |
-"focus" accessibility event that assistive technology could listen |
-to. A screen reader might then announce the name and current value of |
-the text field. A magnifier might zoom the screen to its bounding |
-box. If the user types some text into the text field, Chromium would |
-fire a "value changed" accessibility event. |
- |
-As with nodes in the accessibility tree, each platform has a slightly different |
-API for accessibility events. On Windows we'd fire EVENT_OBJECT_FOCUS for |
-a focus change, and on Mac we'd fire @"AXFocusedUIElementChanged". |
-Those are pretty similar. Sometimes they're quite different - to support |
-live regions (notifications that certain key parts of a web page have changed), |
-on Mac we simply fire @"AXLiveRegionChanged", but on Windows we need to |
-fire IA2_EVENT_TEXT_INSERTED and IA2_EVENT_TEXT_REMOVED events individually |
-on each affected node within the changed region, with additional attributes |
-like "container-live:polite" to indicate that the affected node was part of |
-a live region. This discussion is not meant to explain all of the technical |
-details but just to illustrate that the concepts are similar, |
-but the details of notifying software on each platform about changes can |
-vary quite a bit. |
- |
-### Accessibility Actions |
- |
-Each native object that implements a platform's native accessibility API |
-supports a number of actions, which are requests from the assistive |
-technology to control or change the UI. This is the opposite of events, |
-which are messages from Chromium to the assistive technology. |
- |
-For example, if the user had a voice control application running, such as |
-Voice Access on Android, the user could just speak the name of one of the |
-buttons on the page, like "Next". Upon recognizing that text and finding |
-that it matches one of the UI elements on the page, the voice control |
-app executes the action to click the button id=6 in Chromium's accessibility |
-tree. Internally we call that action "do default" rather than click, since |
-it represents the default action for any type of control. |
- |
-Other examples of actions include setting focus, changing the value of |
-a control, and scrolling the page. |
- |
-### Parameterized attributes |
- |
-In addition to accessibility attributes, events, and actions, native |
-accessibility APIs often have so-called "parameterized attributes". |
-The most common example of this is for text - for example there may be |
-a function to retrieve the bounding box for a range of text, or a |
-function to retrieve the text properties (font family, font size, |
-weight, etc.) at a specific character position. |
- |
-Parameterized attributes are particularly tricky to implement because |
-of Chromium's multi-process architecture. More on this in the next section. |
- |
-## Chromium's multi-process architecture |
- |
-Native accessibility APIs tend to have a *functional* interface, where |
-Chromium implements an interface for a canonical accessible object that |
-includes methods to return various attributes, walk the tree, or perform |
-an action like click(), focus(), or setValue(...). |
- |
-In contrast, the web has a largely *declarative* interface. The shape |
-of the accessibility tree is determined by the DOM tree (occasionally |
-influenced by CSS), and the accessible semantics of a DOM element can |
-be modified by adding ARIA attributes. |
- |
-One important complication is that all of these native accessibility APIs |
-are *synchronous*, while Chromium is multi-process, with the contents of |
-each web page living in a different process than the process that |
-implements Chromium's UI and the native accessibility APIs. Furthermore, |
-the renderer processes are *sandboxed*, so they can't implement |
-operating system APIs directly. |
- |
-If you're unfamiliar with Chrome's multi-process architecture, see |
-[this blog post introducing the concept]( |
-https://blog.chromium.org/2008/09/multi-process-architecture.html) or |
-[the design doc on chromium.org]( |
-https://www.chromium.org/developers/design-documents/multi-process-architecture) |
-for an intro. |
- |
-Chromium's multi-process architecture means that we can't implement |
-accessibility APIs the same way that a single-process browser can - |
-namely, by calling directly into the DOM to compute the result of each |
-API call. For example, on some operating systems there might be an API |
-to get the bounding box for a particular range of characters on the |
-page. In other browsers, this might be implemented by creating a DOM |
-selection object and asking for its bounding box. |
- |
-That implementation would be impossible in Chromium because it'd require |
-blocking the main thread while waiting for a response from the renderer |
-process that implements that web page's DOM. (Not only is blocking the |
-main thread strictly disallowed, but the latency of doing this for every |
-API call makes it prohibitively slow anyway.) Instead, Chromium takes an |
-approach where a representation of the entire accessibility tree is |
-cached in the main process. Great care needs to be taken to ensure that |
-this representation is as concise as possible. |
- |
-In Chromium, we build a data structure representing all of the |
-information for a web page's accessibility tree, send the data |
-structure from the renderer process to the main browser process, cache |
-it in the main browser process, and implement native accessibility |
-APIs using solely the information in that cache. |
- |
-As the accessibility tree changes, tree updates and accessibility events |
-get sent from the renderer process to the browser process. The browser |
-cache is updated atomically in the main thread, so whenever an external |
-client (like assistive technology) calls an accessibility API function, |
-we're always returning something from a complete and consistent snapshot |
-of the accessibility tree. From time to time, the cache may lag what's |
-in the renderer process by a fraction of a second. |
- |
-Here are some of the specific challenges faced by this approach and |
-how we've addressed them. |
- |
-### Sparse data |
- |
-There are a *lot* of possible accessibility attributes for any given |
-node in an accessibility tree. For example, there are more than 150 |
-unique accessibility API methods that Chrome implements on the Windows |
-platform alone. We need to implement all of those APIs, many of which |
-request rather rare or obscure attributes, but storing all possible |
-attribute values in a single struct would be quite wasteful. |
- |
-To avoid each accessible node object containing hundreds of fields the |
-data for each accessibility node is stored in a relatively compact |
-data structure, ui::AXNodeData. Every AXNodeData has an integer ID, a |
-role enum, and a couple of other mandatory fields, but everything else |
-is stored in attribute arrays, one for each major data type. |
- |
-``` |
-struct AXNodeData { |
- int32_t id; |
- AXRole role; |
- ... |
- std::vector<std::pair<AXStringAttribute, std::string>> string_attributes; |
- std::vector<std::pair<AXIntAttribute, int32_t>> int_attributes; |
- ... |
-} |
-``` |
- |
-So if a text field has a placeholder attribute, we can store |
-that by adding an entry to `string_attributes` with an attribute |
-of ui::AX_ATTR_PLACEHOLDER and the placeholder string as the value. |
- |
-### Incremental tree updates |
- |
-Web pages change frequently. It'd be terribly inefficient to send a |
-new copy of the accessibility tree every time any part of it changes. |
-However, the accessibility tree can change shape in complicated ways - |
-for example, whole subtrees can be reparented dynamically. |
- |
-Rather than writing code to deal with every possible way the |
-accessibility tree could be modified, Chromium has a general-purpose |
-tree serializer class that's designed to send small incremental |
-updates of a tree from one process to another. The tree serializer has |
-just a few requirements: |
- |
-* Every node in the tree must have a unique integer ID. |
-* The tree must be acyclic. |
-* The tree serializer must be notified when a node's data changes. |
-* The tree serializer must be notified when the list of child IDs of a |
- node changes. |
- |
-The tree serializer doesn't know anything about accessibility attributes. |
-It keeps track of the previous state of the tree, and every time the tree |
-structure changes (based on notifications of a node changing or a node's |
-children changing), it walks the tree and builds up an incremental tree |
-update that serializes as few nodes as possible. |
- |
-In the other process, the Unserialization code applies the incremental |
-tree update atomically. |
- |
-### Text bounding boxes |
- |
-One challenge faced by Chromium is that accessibility clients want to be |
-able to query the bounding box of an arbitrary range of text - not necessarily |
-just the current cursor position or selection. As discussed above, it's |
-not possible to block Chromium's main browser process while waiting for this |
-information from Blink, so instead we cache enough information to satisfy these |
-queries in the accessibility tree. |
- |
-To compactly store the bounding box of every character on the page, we |
-split the text into *inline text boxes*, sometimes called *text runs*. |
-For example, in a typical paragraph, each line of text would be its own |
-inline text box. In general, an inline text box or text run contians a |
-sequence of text characters that are all oriented in the same direction, |
-in a line, with the same font, size, and style. |
- |
-Each inline text box stores its own bounding box, and then the relative |
-x-coordinate of each character in its text (assuming left-to-right). |
-From that it's possible to compute the bounding box |
-of any individual character. |
- |
-The inline text boxes are part of Chromium's internal accessibility tree. |
-They're used purely internally and aren't ever exposed directly via any |
-native accessibility APIs. |
- |
-For example, suppose that a document contains a text field with the text |
-"Hello world", but the field is narrow, so "Hello" is on the first line and |
-"World" is on the second line. Internally Chromium's accessibility tree |
-might look like this: |
- |
-``` |
-staticText location=(8, 8) size=(38, 36) name='Hello world' |
- inlineTextBox location=(0, 0) size=(36, 18) name='Hello ' characterOffsets=12,19,23,28,36 |
- inlineTextBox location=(0, 18) size=(38, 18) name='world' characterOffsets=12,20,25,29,37 |
-``` |
- |
-### Scrolling, transformations, and animation |
- |
-Native accessibility APIs typically want the bounding box of every element in the |
-tree, either in window coordinates or global screen coordinates. If we |
-stored the global screen coordinates for every node, we'd be constantly |
-re-serializing the whole tree every time the user scrolls or drags the |
-window. |
- |
-Instead, we store the bounding box of each node in the accessibility tree |
-relative to its *offset container*, which can be any ancestor. If no offset |
-container is specified, it's assumed to be the root of the tree. |
- |
-In addition, any offset container can contain scroll offsets, which can be |
-used to scroll the bounding boxes of anything in that subtree. |
- |
-Finally, any offset container can also include an arbitrary 4x4 transformation |
-matrix, which can be used to represent arbitrary 3-D rotations, translations, and |
-scaling, and more. The transformation matrix applies to the whole subtree. |
- |
-Storing coordinates this way means that any time an object scrolls, moves, or |
-animates its position and scale, only the root of the scrolling or animation |
-needs to post updates to the accessibility tree. Everything in the subtree |
-remains valid relative to that offset container. |
- |
-Computing the global screen coordinates for an object in the accessibility |
-tree just means walking up its ancestor chain and applying offsets and |
-occasionally multiplying by a 4x4 matrix. |
- |
-### Site isolation / out-of-process iframes |
- |
-At one point in time, all of the content of a single Tab or other web view |
-was contained in the same Blink process, and it was possible to serialize |
-the accessibility tree for a whole frame tree in a single pass. |
- |
-Today the situation is a bit more complicated, as Chromium supports |
-out-of-process iframes. (It also supports "browser plugins" such as |
-the `<webview>` tag in Chrome packaged apps, which embeds a whole |
-browser inside a browser, but for the purposes of accessibility this |
-is handled the same as frames.) |
- |
-Rather than a mix of in-process and out-of-process frames that are handled |
-differently, Chromium builds a separate independent accessibility tree |
-for each frame. Each frame gets its own tree ID, and it keeps track of |
-the tree ID of its parent frame (if any) and any child frames. |
- |
-In Chrome's main browser process, the accessibility trees for each frame |
-are cached separately, and when an accessibility client (assistive |
-technology) walks the accessibility tree, Chromium dynamically composes |
-all of the frames into a single virtual accessibility tree on the fly, |
-using those aforementioned tree IDs. |
- |
-The node IDs for accessibility trees only need to be unique within a |
-single frame. Where necessary, separate unique IDs are used within |
-Chrome's main browser process. In Chromium accessibility, a "node ID" |
-always means that ID that's only unique within a frame, and a "unique ID" |
-means an ID that's globally unique. |
- |
-## Blink |
- |
-Blink constructs an accessibility tree (a hierarchy of [WebAXObject]s) from the |
-page it is rendering. WebAXObject is the public API wrapper around [AXObject], |
-which is the core class of Blink's accessibility tree. AXObject is an abstract |
-class; the most commonly used concrete subclass of it is [AXNodeObject], which |
-wraps a [Node]. In turn, most AXNodeObjects are actually [AXLayoutObject]s, |
-which wrap both a [Node] and a [LayoutObject]. Access to the LayoutObject is |
-important because some elements are only in the AXObject tree depending on their |
-visibility, geometry, linewrapping, and so on. There are some subclasses of |
-AXLayoutObject that implement special-case logic for specific types of Node. |
-There are also other subclasses of AXObject, which are mostly used for testing. |
- |
-Note that not all AXLayoutObjects correspond to actual Nodes; some are synthetic |
-layout objects which group related inline elements or similar. |
- |
-The central class responsible for dealing with accessibility events in Blink is |
-[AXObjectCacheImpl], which is responsible for caching the corresponding |
-AXObjects for Nodes or LayoutObjects. This class has many methods named |
-`handleFoo`, which are called throughout Blink to notify the AXObjectCacheImpl |
-that it may need to update its tree. Since this class is already aware of all |
-accessibility events in Blink, it is also responsible for relaying accessibility |
-events from Blink to the embedding content layer. |
- |
-## The content layer |
- |
-The content layer lives on both sides of the renderer/browser split. The content |
-layer translates WebAXObjects into [AXContentNodeData], which is a subclass of |
-[ui::AXNodeData]. The ui::AXNodeData class and related classes are Chromium's |
-cross-platform accessibility tree. The translation is implemented in |
-[BlinkAXTreeSource]. This translation happens on the renderer side, so the |
-ui::AXNodeData tree now needs to be sent to the browser, which is done by |
-sending [AccessibilityHostMsg_EventParams] with the payload being serialized |
-delta-updates to the tree, so that changes that happen on the renderer side can |
-be reflected on the browser side. |
- |
-On the browser side, these IPCs are received by [RenderFrameHostImpl], and then |
-usually forwarded to [BrowserAccessibilityManager] which is responsible for: |
- |
-1. Merging AXNodeData trees into one tree of [BrowserAccessibility] objects, |
- by linking to other BrowserAccessibilityManagers. This is important because |
- each page has its own accessibility tree, but each Chromium *window* must |
- have only one accessibility tree, so trees from multiple pages need to be |
- combined (possibly also with trees from Views UI). |
-2. Dispatching outgoing accessibility events to the platform's accessibility |
- APIs. This is done in the platform-specific subclasses of |
- BrowserAccessibilityManager, in a method named `NotifyAccessibilityEvent`. |
-3. Dispatching incoming accessibility actions to the appropriate recipient, via |
- [BrowserAccessibilityDelegate]. For messages destined for a renderer, |
- [RenderFrameHostImpl], which is a BrowserAccessibilityDelegate, is |
- responsible for sending appropriate `AccessibilityMsg_Foo` IPCs to the |
- renderer, where they will be received by [RenderAccessibilityImpl]. |
- |
-On Chrome OS, RenderFrameHostImpl does not route events to |
-BrowserAccessibilityManager at all, since there is no platform screenreader |
-outside Chromium to integrate with. |
- |
-## Views |
- |
-Views generates a [NativeViewAccessibility] for each View, which is used as the |
-delegate for an [AXPlatformNode] representing that View. This part is relatively |
-straightforward, but then the generated tree must be combined with the web |
-accessibility tree, which is handled by BrowserAccessibilityManager. |
- |
-## WebUI |
- |
-Since WebUI surfaces have renderer processes as normal, WebUI accessibility goes |
-through the blink-to-content-to-platform pipeline described above. Accessibility |
-for WebUI is largely implemented in JavaScript in [webui-js]; these classes take |
-care of adding ARIA attributes and so on to DOM nodes as needed. |
- |
-## The Chrome OS layer |
- |
-The accessibility tree is also exposed via the [chrome.automation API], which |
-gives extension JavaScript access to the accessibility tree, events, and |
-actions. This API is implemented in C++ by [AutomationInternalCustomBindings], |
-which is renderer-side code, and in JavaScript by the [automation API]. The API |
-is defined by [automation.idl], which must be kept synchronized with |
-[ax_enums.idl]. |
- |
-[AccessibilityHostMsg_EventParams]: https://cs.chromium.org/chromium/src/content/common/accessibility_messages.h?sq=package:chromium&l=75 |
-[AutomationInternalCustomBindings]: https://cs.chromium.org/chromium/src/chrome/renderer/extensions/automation_internal_custom_bindings.h |
-[AXContentNodeData]: https://cs.chromium.org/chromium/src/content/common/ax_content_node_data.h |
-[AXLayoutObject]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.h |
-[AXNodeObject]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h |
-[AXObject]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/accessibility/AXObject.h |
-[AXObjectCacheImpl]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h |
-[AXPlatformNode]: https://cs.chromium.org/chromium/src/ui/accessibility/platform/ax_platform_node.h |
-[AXTreeSerializer]: https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree_serializer.h |
-[BlinkAXTreeSource]: https://cs.chromium.org/chromium/src/content/renderer/accessibility/blink_ax_tree_source.h |
-[BrowserAccessibility]: https://cs.chromium.org/chromium/src/content/browser/accessibility/browser_accessibility.h |
-[BrowserAccessibilityDelegate]: https://cs.chromium.org/chromium/src/content/browser/accessibility/browser_accessibility_manager.h?sq=package:chromium&l=64 |
-[BrowserAccessibilityManager]: https://cs.chromium.org/chromium/src/content/browser/accessibility/browser_accessibility_manager.h |
-[LayoutObject]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/layout/LayoutObject.h |
-[NativeViewAccessibility]: https://cs.chromium.org/chromium/src/ui/views/accessibility/native_view_accessibility.h |
-[Node]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/dom/Node.h |
-[RenderAccessibilityImpl]: https://cs.chromium.org/chromium/src/content/renderer/accessibility/render_accessibility_impl.h |
-[RenderFrameHostImpl]: https://cs.chromium.org/chromium/src/content/browser/frame_host/render_frame_host_impl.h |
-[ui::AXNodeData]: https://cs.chromium.org/chromium/src/ui/accessibility/ax_node_data.h |
-[WebAXObject]: https://cs.chromium.org/chromium/src/third_party/WebKit/public/web/WebAXObject.h |
-[automation API]: https://cs.chromium.org/chromium/src/chrome/renderer/resources/extensions/automation |
-[automation.idl]: https://cs.chromium.org/chromium/src/chrome/common/extensions/api/automation.idl |
-[ax_enums.idl]: https://cs.chromium.org/chromium/src/ui/accessibility/ax_enums.idl |
-[chrome.automation API]: https://developer.chrome.com/extensions/automation |
-[webui-js]: https://cs.chromium.org/chromium/src/ui/webui/resources/js/cr/ui/ |
+* [ChromeVox for Developers](accessibility/chromevox.md) |
+* [ChromeVox on Desktop Linux](accessibility/chromevox_on_desktop_linux.md) |
+* [Updating brltty braille drivers](accessibility/brltty.md) |
+* [Updating the patts speech synthesis engine](accessibility/patts.md) |