Accessibility/Accessible Table Implementation

From MozillaWiki
Jump to: navigation, search

Proposals

ATK/AT-SPI implementations

gtkTable

  • getAccessibleAt(0, 0) returns the top-left data cell. Row and column headers cannot be fetched using using this method.
  • getIndexAt(0,0) returns the child index under the table accessible of the top-left data cell. Again, row and column headers cannot be fetched using this method.
  • getRowHeader(n), getColumnHeader(n) return the accessible representing the one and only one header on the nth row and nth column. Only one header is allowed per row and column in gtk as far as I know.
  • getRowDescription(n), getColumnDescription(n) return the accessible text or name on the one and only one header on the nth row and nth column. This is equivalent to getRowHeader(n).name or getRowHeader(n).queryInterface(<whatever the text idd is>).getText(0,-1). I can't tell which one gail is doing for us without looking at the gail code.
  • I have never seen a row or column span in gtkTables, so it's hard to test getRowExtentAt and getColumnExtentAt. Everything returns 1 as expected.
  • Once the table receives focus, the arrow keys move the selection within the table. Pressing up when selection is in the top row has no effect: selection never reaches the headers. Pressing Shift-Tab does move focus to the header on the currently active column though. Once one of the headers has focus, the left/right arrow keys move focus among the headers. Interestingly, pressing down arrow from any of the headers gives focus back to the table and selects the top cell in the column under the header losing focus.
  • The table container fires focus: events. Moving the active selection box within the table causes object:active-descendant-changed events. Moving the focus among the headers causes focus: events.

OpenOffice.org Calc (2.0.4)

  • getAccessibleAt(0, 0) returns the top-left data cell. Row and column headers cannot be fetched using using this method.
  • getIndexAt(0,0) returns the child index under the table accessible of the top-left data cell. Again, row and column headers cannot be fetched using this method.
  • The accessible name on any data cell is it's header (e.g., 'Cell A1'). The accessible text is its shown content.
  • getRowHeader(n), getColumnHeader(n) return None in all cases.
  • getRowDescription(n), getColumnDescription(n) return empty strings in all cases.
  • getRowExtentAt and getColumnExtentAt return 1 in all cases, even for cells that span rows and columns.
  • Once the table receives focus, the arrow keys move the selection within the table. You can never give the headers focus.
  • The table container fires focus: events. Moving the active selection box within the table causes object:active-descendant-changed events.

Firefox bookmark tree table (XUL)

  • getAccessibleAt(0, 0) returns the top-left data cell. Row and column headers cannot be fetched using using this method.
  • getIndexAt(0,0) returns the child index under the table accessible of the top-left data cell. Again, row and column headers cannot be fetched using this method.
  • getRowHeader(n), getColumnHeader(n) return the accessible representing the header on the nth row and nth column. I have not found a XUL table with more than one header.
  • getRowDescription(n), getColumnDescription(n) return the accessible text or name on the one and only one header on the nth row and nth column. This is equivalent to getRowHeader(n).name or getRowHeader(n).queryInterface(<whatever the text idd is>).getText(0,-1).
  • Cells that visually span columns (e.g., a separator in the bookmarks tree table), exist as separate cells in the table. getRowExtentAt(r,c) and getColumnExtentAt(r,c) return 1 for all rows and columns falling into the spanning cell.
  • The table container fires no events. Moving the active selection box up and down within the table causes focus events on the cells in the first column. Focus cannot traverse other columns.

Firefox ARIA table

http://www.mozilla.org/access/dhtml/spreadsheet

  • getAccessibleAt(0, 0) returns the top-left cell, even if it is marked as a header in the HTML and has ROLE_COLUMN_HEADER in AT-SPI.
  • getIndexAt(0,0) returns the child index under the table accessible of the top-left cell. Again, row and column headers are fetched using this method.
  • getRowHeader(n), getColumnHeader(n) always return None.
  • getRowDescription(n), getColumnDescription(n) always return an empty string.
  • This example does not have any spanning cells for me to test.
  • Pressing Tab first causes focus: to fire on an unknown accessible even though the focus border appears on the table. Pressing Tab again fires two focus: events in succession, one on the table container and the other on the top-left cell in the table. Arrowing around the table causes additional focus: events to fire.

Oddly enough, when I keep arrowing down, out of the bottom of the table, Firefox caret is on, even though I never turned it on manually.

Firefox static HTML

  • getAccessibleAt(0, 0) returns the top-left cell, even if it is marked as a header in the HTML source.
  • getIndexAt(0,0) returns the child index under the table accessible of the top-left cell. Again, row and column headers are fetched using this method.
  • getColumnHeader(n) returns the accessible of the table cell in the first row of the given column no matter what tag it is or what attributes it has. getRowHeader(n) returns nothing, at least when row headers are marked with <th>. When there are TH cells or header attributes that point to TD cells with id attributes referenced by the header attributes, FF should return these header cells in the getColumnHeader(n) or getRowHeader(n) calls. Testcases:[1] [2]* getRowDescription(n), getColumnDescription(n) always return an empty string.
  • getRowExtentAt(r,c) and getColumnExtentAt(r,c) return the appropriate extents for all row and column offsets falling in the same cell.
  • For cells with extents:
    • getAccessibleAt(r,c) returns the same accessible object (a1 == b1) for all row and column offsets falling in the same cell.
    • getIndexAt(r, c) returns the same index for all row and column offsets falling in the same cell.
    • getRowAtIndex(i), getColumnAtIndex(i) return the upper left most offset applicable to the spanning cell.
  • Events do not apply to a static HTML table.

Implementations of list tables in Win apps

For list tables (multi col lists) there are various implementations.

  1. Native Win like File Explorer or File Open: The acc name is the text from the first col. The acc desc contains the remaining column text with the text from each column prefixed with the text from the col header. The last child of the list is a WINDOW with a child of LIST with children of COLUMN_HEADERs. Those col headers have acc names and actions named "Click". Larry and I would perfer "Sort". For some reason I can't navigate to that last child with Inspect or the navigation menu item of AccExplorer - but AccExplorer does show it as the last child. I don't think there is any directly obtainable n of m info without writing some app specific code, i.e. accounting for the extra non list item sibling.
  2. Eclipse: Just like the native list but the list of col headers is the prior sibling to the list of list items.
  3. Firefox: The full list item (all the col data) is in the acc name, n of m info is in the acc desc, the list of col headers is the first child of the main list, the col headers have actions named "Click". And I can nav to the list of col headers with Inspect.
  4. SODC: The acc name is the full line of text (all the col data). The list has one next sibling that is a COLUMN_HEADER with no name and no action. The n of m info is coded in IA2::groupPosition.

Implementation Problems

getIndexInParent Method Synchronization

When we deal with tables, AT requires that the method getIndexInParent of accessible object is synchronized with methods of the accessible table interface. It's valid for ATK but it makes sense for IA2 as well. In detail,, when getIndexInParent is called on any table cell, it should return an index that is then being used in getRowAtIndex and getColumnAtIndex methods. This rule assumes the table cells must be direct children of the table accessible. Firefox table implementation broke this rule in some cases.

HTML static tables in ATK

Let's consider the following HTML table:

<table>
  <tr>
    <td>cell1</td><td>cell2</td>
  </tr>
  <tr role="some_aria_role">
    <td>cell3</td><td>cell4</td>
  </tr>
</table>

The accessible tree should look like this:

table
  cell1
  cell2
  some_aria_role
    cell3
    cell4

Here we get the problem. We should expose an accessible for html:tr element since it's accessible. Therefore, the rule above is broken.

List Tables in MSAA/IA2

It was mentioned above that tables in MSAA/IA2 are exposed as lists. Since table cells can contain anything, we can't just expose name and description on the table accessible, and we should expose accessible for table, table rows and table cells. Therefore we will break synchronization of IAccessibleTable interface methods with the getIndexInParent method like we have for the case of static HTML table in ATK.

Outline in MSAA/IA2

Currently XUL tree is exposed as outline. We don't expose any cells but the primary one. Therefore we lose some information (for example bug 360510 where the cell with checkbox isn't exposed).

Possible approaches

We can't extend ATK and IA2 in order to add a new method in place of getIndexInParent. Instead, we could use accessible attributes to put the cell's index in the table on each cell. The disadvantage of this approach is the requirement to parse the attribute string. Another disadvantage is that old screen readers won't work with that.