Pro Yearly is on sale from $80 to $50! »

What even is a table? A quick look at Accessibility APIs

5ce91fa49a613cbc3e20d5f96856473f?s=47 Edd S
September 12, 2015

What even is a table? A quick look at Accessibility APIs

Lets look at the tools that you have available to you today to ensure that you are doing the best job to make your site as accessible as possible. A quick dive into accessibility apis and trying to answer the question, what even is a table?

5ce91fa49a613cbc3e20d5f96856473f?s=128

Edd S

September 12, 2015
Tweet

Transcript

  1. Edd Sowden
 Senior Developer 
 Government Digital Service
 @edds

  2. Accessibility and how to get the most from your screenreader

    ! What even is a table? A quick look at Accessibility APIs GDS @edds
  3. * I’m from the Government GDS @edds

  4. We’re a team at the heart of government building digital

    public services GDS @edds
  5. * We started by building GOV.UK GDS @edds

  6. * The best place to find government services and information

    GDS @edds
  7. GDS @edds

  8. GDS @edds

  9. GDS @edds

  10. GDS @edds

  11. GDS @edds

  12. GOV.UK should work for everyone GDS @edds

  13. I wrote a blog post

  14. dimensions = ga:browser,ga:hour,ga:dayOfWeek metrics = ga:users filters = ga:browser==Internet Explorer

    Which would get you something like: (view with your own data in the Query Explorer) Then using the TSV export button in the Query Explorer, found at the bottom of the page, I can import the results into a Google Sheets and produce a quick graph of the data. For another example if we had a page which used a query string to let users filter by a date field. You could get back all the different dates people filtered by: Browser Hour Day of Week Users Internet Explorer 00 0 5895 Internet Explorer 00 1 6814 Internet Explorer 00 2 8115 Internet Explorer 00 3 8000 Internet Explorer 00 4 8077 … … … … GDS @edds
  15. GDS @edds

  16. table { display: block; overflow-x: scroll; } GDS @edds

  17. GDS @edds

  18. What does this do to accessibility software? GDS @edds

  19. Listen to your websites

  20. How many of you use Apple products regularly? GDS @edds

  21. GDS @edds How many of you test your websites in

    a screenreader?
  22. Voiceover comes with OS X and iOS GDS @edds

  23. The Voiceover Training is really good GDS @edds

  24. Let’s listen to it before I made the change GDS

    @edds
  25. GDS @edds

  26. And once I had added the extra CSS… GDS @edds

  27. GDS @edds

  28. The CSS display property affects how accessibility tech interprets the

    element GDS @edds
  29. <div class=“table-wrapper”> <table> … </table> </div> GDS @edds

  30. I had lunch

  31. “Can’t you just set an aria role of table?” @jaffathecake

    GDS @edds
  32. <table role=“table”> GDS @edds

  33. <table> <tr><td>Cell</td></tr> </table> GDS @edds Minimum viable table:

  34. GDS @edds

  35. GDS @edds

  36. GDS @edds chrome://flags/#enable-devtools-experiments

  37. GDS @edds

  38. GDS @edds

  39. GDS @edds

  40. GDS @edds

  41. GDS @edds

  42. So our minimum viable table isn’t a table? GDS @edds

  43. Let’s have a quick recap on Accessibility APIs GDS @edds

  44. GDS @edds

  45. GDS @edds

  46. Accessibility APIs! ! Windows 7: MSAA/IAccessible OSX: NSAccessibility iOS: UI

    Accessibility Android: Accessibility Framework GDS @edds
  47. role name state children GDS @edds

  48. GDS @edds HTML to Platform Accessibility APIs Implementation Guide W3C

    Working Draft 01 October 2013 This version: http://www.w3.org/TR/2013/WD-html-aapi-20131001/ Latest published version: http://www.w3.org/TR/html-aapi/ Latest editor's draft: http://rawgithub.com/w3c/html-api-map/master/index.html Previous version: http://www.w3.org/TR/2012/WD-html-aapi-20130901/ Editors: Steve Faulkner, The Paciello Group, sfaulkner@paciellogroup.com Cynthia Shelly, Microsoft, cyns@microsoft.com Jason Kiss, New Zealand Government, jason.kiss@dia.govt.nz Alexander Surkov, Mozilla Foundation, asurkov@mozilla.com Copyright © 2011-2013 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C liability, trademark and document use rules apply. Select text and file a bug
  49. GDS @edds svg table table HTML4 Yes HTML5 Yes WAI-ARIA

    none MSAA + UIA Express Role: ROLE_SYSTEM_TABLE Use MSAA or UIA guidance MSAA + IAccessible2 Role: ROLE_SYSTEM_TABLE Relations: IA2_RELATION_LABELLED_BY by child caption if any Interface: IAccessibleTable2 UIA Control Type and Other Features Control Type: Data Grid Control Pattern: Table AT-SPI Role: ROLE_TABLE Relations: RELATION_LABELLED_BY by child caption if any Interface: Table AX AXRole: AXTable AXSubrole: (nil) AXRoleDescription: table tbody td
  50. Browsers use the DOM and CSS to generate an accessibility

    tree. This is then passed onto accessibility APIs GDS @edds
  51. So what does it look like for a “real” table?

    GDS @edds
  52. GDS @edds

  53. What do other browsers think of our minimum viable table?

    GDS @edds
  54. GDS @edds

  55. GDS @edds

  56. GDS @edds

  57. GDS @edds

  58. GDS @edds

  59. GDS @edds

  60. GDS @edds

  61. GDS @edds

  62. JAWS GDS @edds

  63. JAWS advances to the next table with the ’t’ shortcut

    GDS @edds
  64. GDS @edds

  65. GDS @edds

  66. GDS @edds <table> <tr><td>Cell</td></tr> </table>

  67. Browsers have heuristics to protect us from table based layouts

    GDS @edds
  68. <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table> GDS @edds

  69. GDS @edds <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table>

  70. <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> [ … 18

    more rows … ] </table> GDS @edds
  71. GDS @edds <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> [

    … 18 more rows … ] </table>
  72. <table summary=“colours”> <tr><td>Green</td></tr> </table> GDS @edds

  73. GDS @edds <table summary=“colours”> <tr><td>Green</td></tr> </table>

  74. <table> <tr> <th>…</th><td>…</td> </tr> <tr> <th>…</th><td>…</td> </tr> </table> GDS @edds

  75. GDS @edds <table> <tr> <th>…</th><td>…</td> </tr> <tr> <th>…</th><td>…</td> </tr> </table>

  76. table { display: block; } ! <table> <tr> <th>…</th><td>…</td> </tr>

    <tr> <th>…</th><td>…</td> </tr> </table> GDS @edds
  77. GDS @edds table { display: block; } ! <table> <tr>

    <th>…</th><td>…</td> </tr> <tr> <th>…</th><td>…</td> </tr> </table>
  78. td { border-bottom: 1px solid; } ! <table> <tr> <td>…</td><td>…</td>

    </tr> <tr> <td>…</td><td>…</td> </tr> </table> GDS @edds
  79. GDS @edds td { border-bottom: 1px solid; } ! <table>

    <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table>
  80. td { background: grey; } ! <table> <tr> <td>…</td><td>…</td> </tr>

    <tr> <td>…</td><td>…</td> </tr> </table> GDS @edds
  81. GDS @edds td { background: grey; } ! <table> <tr>

    <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table>
  82. table { border-collapse: collapse; } td { background: grey; }

    ! <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table> GDS @edds
  83. GDS @edds table { border-collapse: collapse; } td { background:

    grey; } ! <table> <tr> <td>…</td><td>…</td> </tr> <tr> <td>…</td><td>…</td> </tr> </table>
  84. Let’s have a quick look under the hood in Chrome

    GDS @edds
  85. GDS @edds 102. bool AXTable::isDataTable() const 103. { 104. if

    (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr<HTMLCollection> bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex);
  86. GDS @edds 102. bool AXTable::isDataTable() const 103. { 104. if

    (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr<HTMLCollection> bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex); 102. bool AXTable::isDataTable() const 103. { 104. if (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr<HTMLCollection> bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex);
  87. GDS @edds 167. // cells have borders, or use attributes

    like headers, abbr, scope or axis 168. table->recalcSectionsIfNeeded(); 169. LayoutTableSection* firstBody = table->firstBody(); 170. if (!firstBody) 171. return false; 172. 173. int numCols = firstBody->numColumns(); 174. int numRows = firstBody->numRows(); 175. 176. // If there's only one cell, it's not a good AXTable candidate. 177. if (numRows == 1 && numCols == 1) 178. return false; 179. 180. // If there are at least 20 rows, we'll call it a data table. 181. if (numRows >= 20) 182. return true; 183. 184. // Store the background color of the table to check against cell's background colors. 185. const ComputedStyle* tableStyle = table->style(); 186. if (!tableStyle) 187. return false; 188. Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); 189. 190. // check enough of the cells to find if the table matches our criteria 191. // Criteria: 192. // 1) must have at least one valid cell (and) 193. // 2) at least half of cells have borders (or) 194. // 3) at least half of cells have different bg colors than the table, and there is cell spacing 195. unsigned validCellCount = 0; 196. unsigned borderedCellCount = 0; 197. unsigned backgroundDifferenceCellCount = 0; 198. unsigned cellsWithTopBorder = 0; 199. unsigned cellsWithBottomBorder = 0; 200. unsigned cellsWithLeftBorder = 0; 201. unsigned cellsWithRightBorder = 0; 167. // cells have borders, or use attributes like headers, abbr, scope or axis 168. table->recalcSectionsIfNeeded(); 169. LayoutTableSection* firstBody = table->firstBody(); 170. if (!firstBody) 171. return false; 172. 173. int numCols = firstBody->numColumns(); 174. int numRows = firstBody->numRows(); 175. 176. // If there's only one cell, it's not a good AXTable candidate. 177. if (numRows == 1 && numCols == 1) 178. return false; 179. 180. // If there are at least 20 rows, we'll call it a data table. 181. if (numRows >= 20) 182. return true; 183. 184. // Store the background color of the table to check against cell's background colors. 185. const ComputedStyle* tableStyle = table->style(); 186. if (!tableStyle) 187. return false; 188. Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); 189. 190. // check enough of the cells to find if the table matches our criteria 191. // Criteria: 192. // 1) must have at least one valid cell (and) 193. // 2) at least half of cells have borders (or) 194. // 3) at least half of cells have different bg colors than the table, and there is cell spacing 195. unsigned validCellCount = 0; 196. unsigned borderedCellCount = 0; 197. unsigned backgroundDifferenceCellCount = 0; 198. unsigned cellsWithTopBorder = 0; 199. unsigned cellsWithBottomBorder = 0; 200. unsigned cellsWithLeftBorder = 0; 201. unsigned cellsWithRightBorder = 0;
  88. GDS @edds 294. return true; 295. 296. // if there

    is less than two valid cells, it's not a data table 297. if (validCellCount <= 1) 298. return false; 299. 300. // half of the cells had borders, it's a data table 301. unsigned neededCellCount = validCellCount / 2; 302. if (borderedCellCount >= neededCellCount 303. || cellsWithTopBorder >= neededCellCount 304. || cellsWithBottomBorder >= neededCellCount 305. || cellsWithLeftBorder >= neededCellCount 306. || cellsWithRightBorder >= neededCellCount) 307. return true; 308. 309. // half had different background colors, it's a data table 310. if (backgroundDifferenceCellCount >= neededCellCount) 311. return true; 312. 313. // Check if there is an alternating row background color indicating a zebra striped style pattern. 314. if (alternatingRowColorCount > 2) { 315. Color firstColor = alternatingRowColors[0]; 316. for (int k = 1; k < alternatingRowColorCount; k++) { 317. // If an odd row was the same color as the first row, its not alternating. 318. if (k % 2 == 1 && alternatingRowColors[k] == firstColor) 319. return false; 320. // If an even row is not the same as the first row, its not alternating. 321. if (!(k % 2) && alternatingRowColors[k] != firstColor) 322. return false; 323. } 324. return true; 325. } 326. 327. return false; 328. } 294. return true; 295. 296. // if there is less than two valid cells, it's not a data table 297. if (validCellCount <= 1) 298. return false; 299. 300. // half of the cells had borders, it's a data table 301. unsigned neededCellCount = validCellCount / 2; 302. if (borderedCellCount >= neededCellCount 303. || cellsWithTopBorder >= neededCellCount 304. || cellsWithBottomBorder >= neededCellCount 305. || cellsWithLeftBorder >= neededCellCount 306. || cellsWithRightBorder >= neededCellCount) 307. return true; 308. 309. // half had different background colors, it's a data table 310. if (backgroundDifferenceCellCount >= neededCellCount) 311. return true; 312. 313. // Check if there is an alternating row background color indicating a zebra striped style pattern. 314. if (alternatingRowColorCount > 2) { 315. Color firstColor = alternatingRowColors[0]; 316. for (int k = 1; k < alternatingRowColorCount; k++) { 317. // If an odd row was the same color as the first row, its not alternating. 318. if (k % 2 == 1 && alternatingRowColors[k] == firstColor) 319. return false; 320. // If an even row is not the same as the first row, its not alternating. 321. if (!(k % 2) && alternatingRowColors[k] != firstColor) 322. return false; 323. } 324. return true; 325. } 326. 327. return false; 328. }
  89. What about the massive fox in the room? GDS @edds

  90. GDS @edds

  91. Firefox seems to be different depending on which screen reader

    you’re using GDS @edds
  92. GDS @edds 922 // performance problems only. Note, currently 'aAllowEmpty'

    flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
  93. GDS @edds 922 // performance problems only. Note, currently 'aAllowEmpty'

    flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table"); 922 // performance problems only. Note, currently 'aAllowEmpty' flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
  94. GDS @edds 1089 1090 if (childIdx > 0 && prevRowColor

    != rowColor) 1091 RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); 1092 } 1093 } 1094 1095 // Check for many rows 1096 const uint32_t kMaxLayoutRows = 20; 1097 if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1089 1090 if (childIdx > 0 && prevRowColor != rowColor) 1091 RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); 1092 } 1093 } 1094 1095 // Check for many rows 1096 const uint32_t kMaxLayoutRows = 20; 1097 if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 {
  95. GDS @edds 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099

    } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1138 Relation rel = HyperTextAccessible::RelationByType(aType); 1139 if (aType == RelationType::LABEL_FOR) 1140 rel.AppendTarget(Parent()); 1141 1142 return rel; 1143 } 1144 1145 role 1146 HTMLCaptionAccessible::NativeRole() 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1138 Relation rel = HyperTextAccessible::RelationByType(aType); 1139 if (aType == RelationType::LABEL_FOR) 1140 rel.AppendTarget(Parent()); 1141 1142 return rel; 1143 } 1144 1145 role 1146 HTMLCaptionAccessible::NativeRole()
  96. So what does this all mean? GDS @edds

  97. Accessibility is more than just markup

  98. Listen to your websites

  99. <div class=“table-wrapper”> <table> … </table> </div> GDS @edds

  100. Thank you!! ! ! Special thanks to: Alice Bartlet, Robin

    Whittleton, Tom Byers, Alex Muller, Léonie Watson, David Singleton, Gemma Leigh and Jake Archibald GDS @edds
  101. Edd Sowden
 Senior Developer 
 Government Digital Service
 @edds