OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 // This employs a heuristic to determine if this table should appear. | 108 // This employs a heuristic to determine if this table should appear. |
109 // Only "data" tables should be exposed as tables. | 109 // Only "data" tables should be exposed as tables. |
110 // Unfortunately, there is no good way to determine the difference | 110 // Unfortunately, there is no good way to determine the difference |
111 // between a "layout" table and a "data" table. | 111 // between a "layout" table and a "data" table. |
112 | 112 |
113 LayoutTable* table = toLayoutTable(m_layoutObject); | 113 LayoutTable* table = toLayoutTable(m_layoutObject); |
114 Node* tableNode = table->node(); | 114 Node* tableNode = table->node(); |
115 if (!isHTMLTableElement(tableNode)) | 115 if (!isHTMLTableElement(tableNode)) |
116 return false; | 116 return false; |
117 | 117 |
118 // Do not consider it a data table if any of its descendants have an ARIA role
. | 118 // Do not consider it a data table if any of its descendants have an ARIA |
| 119 // role. |
119 HTMLTableElement* tableElement = toHTMLTableElement(tableNode); | 120 HTMLTableElement* tableElement = toHTMLTableElement(tableNode); |
120 if (elementHasAriaRole(tableElement->tHead())) | 121 if (elementHasAriaRole(tableElement->tHead())) |
121 return false; | 122 return false; |
122 if (elementHasAriaRole(tableElement->tFoot())) | 123 if (elementHasAriaRole(tableElement->tFoot())) |
123 return false; | 124 return false; |
124 | 125 |
125 HTMLCollection* bodies = tableElement->tBodies(); | 126 HTMLCollection* bodies = tableElement->tBodies(); |
126 for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { | 127 for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { |
127 Element* bodyElement = bodies->item(bodyIndex); | 128 Element* bodyElement = bodies->item(bodyIndex); |
128 if (elementHasAriaRole(bodyElement)) | 129 if (elementHasAriaRole(bodyElement)) |
129 return false; | 130 return false; |
130 } | 131 } |
131 | 132 |
132 HTMLTableRowsCollection* rows = tableElement->rows(); | 133 HTMLTableRowsCollection* rows = tableElement->rows(); |
133 unsigned rowCount = rows->length(); | 134 unsigned rowCount = rows->length(); |
134 for (unsigned rowIndex = 0; rowIndex < rowCount; ++rowIndex) { | 135 for (unsigned rowIndex = 0; rowIndex < rowCount; ++rowIndex) { |
135 HTMLTableRowElement* rowElement = rows->item(rowIndex); | 136 HTMLTableRowElement* rowElement = rows->item(rowIndex); |
136 if (elementHasAriaRole(rowElement)) | 137 if (elementHasAriaRole(rowElement)) |
137 return false; | 138 return false; |
138 HTMLCollection* cells = rowElement->cells(); | 139 HTMLCollection* cells = rowElement->cells(); |
139 for (unsigned cellIndex = 0; cellIndex < cells->length(); ++cellIndex) { | 140 for (unsigned cellIndex = 0; cellIndex < cells->length(); ++cellIndex) { |
140 if (elementHasAriaRole(cells->item(cellIndex))) | 141 if (elementHasAriaRole(cells->item(cellIndex))) |
141 return false; | 142 return false; |
142 } | 143 } |
143 } | 144 } |
144 | 145 |
145 // If there is a caption element, summary, THEAD, or TFOOT section, it's most
certainly a data table | 146 // If there is a caption element, summary, THEAD, or TFOOT section, it's most |
| 147 // certainly a data table |
146 if (!tableElement->summary().isEmpty() || tableElement->tHead() || | 148 if (!tableElement->summary().isEmpty() || tableElement->tHead() || |
147 tableElement->tFoot() || tableElement->caption()) | 149 tableElement->tFoot() || tableElement->caption()) |
148 return true; | 150 return true; |
149 | 151 |
150 // if someone used "rules" attribute than the table should appear | 152 // if someone used "rules" attribute than the table should appear |
151 if (!tableElement->rules().isEmpty()) | 153 if (!tableElement->rules().isEmpty()) |
152 return true; | 154 return true; |
153 | 155 |
154 // if there's a colgroup or col element, it's probably a data table. | 156 // if there's a colgroup or col element, it's probably a data table. |
155 if (Traversal<HTMLTableColElement>::firstChild(*tableElement)) | 157 if (Traversal<HTMLTableColElement>::firstChild(*tableElement)) |
(...skipping 10 matching lines...) Expand all Loading... |
166 int numRows = firstBody->numRows(); | 168 int numRows = firstBody->numRows(); |
167 | 169 |
168 // If there's only one cell, it's not a good AXTable candidate. | 170 // If there's only one cell, it's not a good AXTable candidate. |
169 if (numRows == 1 && numCols == 1) | 171 if (numRows == 1 && numCols == 1) |
170 return false; | 172 return false; |
171 | 173 |
172 // If there are at least 20 rows, we'll call it a data table. | 174 // If there are at least 20 rows, we'll call it a data table. |
173 if (numRows >= 20) | 175 if (numRows >= 20) |
174 return true; | 176 return true; |
175 | 177 |
176 // Store the background color of the table to check against cell's background
colors. | 178 // Store the background color of the table to check against cell's background |
| 179 // colors. |
177 const ComputedStyle* tableStyle = table->style(); | 180 const ComputedStyle* tableStyle = table->style(); |
178 if (!tableStyle) | 181 if (!tableStyle) |
179 return false; | 182 return false; |
180 Color tableBGColor = | 183 Color tableBGColor = |
181 tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); | 184 tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); |
182 | 185 |
183 // check enough of the cells to find if the table matches our criteria | 186 // check enough of the cells to find if the table matches our criteria |
184 // Criteria: | 187 // Criteria: |
185 // 1) must have at least one valid cell (and) | 188 // 1) must have at least one valid cell (and) |
186 // 2) at least half of cells have borders (or) | 189 // 2) at least half of cells have borders (or) |
187 // 3) at least half of cells have different bg colors than the table, and th
ere is cell spacing | 190 // 3) at least half of cells have different bg colors than the table, and |
| 191 // there is cell spacing |
188 unsigned validCellCount = 0; | 192 unsigned validCellCount = 0; |
189 unsigned borderedCellCount = 0; | 193 unsigned borderedCellCount = 0; |
190 unsigned backgroundDifferenceCellCount = 0; | 194 unsigned backgroundDifferenceCellCount = 0; |
191 unsigned cellsWithTopBorder = 0; | 195 unsigned cellsWithTopBorder = 0; |
192 unsigned cellsWithBottomBorder = 0; | 196 unsigned cellsWithBottomBorder = 0; |
193 unsigned cellsWithLeftBorder = 0; | 197 unsigned cellsWithLeftBorder = 0; |
194 unsigned cellsWithRightBorder = 0; | 198 unsigned cellsWithRightBorder = 0; |
195 | 199 |
196 Color alternatingRowColors[5]; | 200 Color alternatingRowColors[5]; |
197 int alternatingRowColorCount = 0; | 201 int alternatingRowColorCount = 0; |
198 | 202 |
199 int headersInFirstColumnCount = 0; | 203 int headersInFirstColumnCount = 0; |
200 for (int row = 0; row < numRows; ++row) { | 204 for (int row = 0; row < numRows; ++row) { |
201 int headersInFirstRowCount = 0; | 205 int headersInFirstRowCount = 0; |
202 for (int col = 0; col < numCols; ++col) { | 206 for (int col = 0; col < numCols; ++col) { |
203 LayoutTableCell* cell = firstBody->primaryCellAt(row, col); | 207 LayoutTableCell* cell = firstBody->primaryCellAt(row, col); |
204 if (!cell) | 208 if (!cell) |
205 continue; | 209 continue; |
206 Node* cellNode = cell->node(); | 210 Node* cellNode = cell->node(); |
207 if (!cellNode) | 211 if (!cellNode) |
208 continue; | 212 continue; |
209 | 213 |
210 if (cell->size().width() < 1 || cell->size().height() < 1) | 214 if (cell->size().width() < 1 || cell->size().height() < 1) |
211 continue; | 215 continue; |
212 | 216 |
213 validCellCount++; | 217 validCellCount++; |
214 | 218 |
215 bool isTHCell = cellNode->hasTagName(thTag); | 219 bool isTHCell = cellNode->hasTagName(thTag); |
216 // If the first row is comprised of all <th> tags, assume it is a data tab
le. | 220 // If the first row is comprised of all <th> tags, assume it is a data |
| 221 // table. |
217 if (!row && isTHCell) | 222 if (!row && isTHCell) |
218 headersInFirstRowCount++; | 223 headersInFirstRowCount++; |
219 | 224 |
220 // If the first column is comprised of all <th> tags, assume it is a data
table. | 225 // If the first column is comprised of all <th> tags, assume it is a data |
| 226 // table. |
221 if (!col && isTHCell) | 227 if (!col && isTHCell) |
222 headersInFirstColumnCount++; | 228 headersInFirstColumnCount++; |
223 | 229 |
224 // in this case, the developer explicitly assigned a "data" table attribut
e | 230 // In this case, the developer explicitly assigned a "data" table |
| 231 // attribute. |
225 if (isHTMLTableCellElement(*cellNode)) { | 232 if (isHTMLTableCellElement(*cellNode)) { |
226 HTMLTableCellElement& cellElement = toHTMLTableCellElement(*cellNode); | 233 HTMLTableCellElement& cellElement = toHTMLTableCellElement(*cellNode); |
227 if (!cellElement.headers().isEmpty() || !cellElement.abbr().isEmpty() || | 234 if (!cellElement.headers().isEmpty() || !cellElement.abbr().isEmpty() || |
228 !cellElement.axis().isEmpty() || !cellElement.scope().isEmpty()) | 235 !cellElement.axis().isEmpty() || !cellElement.scope().isEmpty()) |
229 return true; | 236 return true; |
230 } | 237 } |
231 | 238 |
232 const ComputedStyle* computedStyle = cell->style(); | 239 const ComputedStyle* computedStyle = cell->style(); |
233 if (!computedStyle) | 240 if (!computedStyle) |
234 continue; | 241 continue; |
235 | 242 |
236 // If the empty-cells style is set, we'll call it a data table. | 243 // If the empty-cells style is set, we'll call it a data table. |
237 if (computedStyle->emptyCells() == EEmptyCells::Hide) | 244 if (computedStyle->emptyCells() == EEmptyCells::Hide) |
238 return true; | 245 return true; |
239 | 246 |
240 // If a cell has matching bordered sides, call it a (fully) bordered cell. | 247 // If a cell has matching bordered sides, call it a (fully) bordered cell. |
241 if ((cell->borderTop() > 0 && cell->borderBottom() > 0) || | 248 if ((cell->borderTop() > 0 && cell->borderBottom() > 0) || |
242 (cell->borderLeft() > 0 && cell->borderRight() > 0)) | 249 (cell->borderLeft() > 0 && cell->borderRight() > 0)) |
243 borderedCellCount++; | 250 borderedCellCount++; |
244 | 251 |
245 // Also keep track of each individual border, so we can catch tables where
most | 252 // Also keep track of each individual border, so we can catch tables where |
246 // cells have a bottom border, for example. | 253 // most cells have a bottom border, for example. |
247 if (cell->borderTop() > 0) | 254 if (cell->borderTop() > 0) |
248 cellsWithTopBorder++; | 255 cellsWithTopBorder++; |
249 if (cell->borderBottom() > 0) | 256 if (cell->borderBottom() > 0) |
250 cellsWithBottomBorder++; | 257 cellsWithBottomBorder++; |
251 if (cell->borderLeft() > 0) | 258 if (cell->borderLeft() > 0) |
252 cellsWithLeftBorder++; | 259 cellsWithLeftBorder++; |
253 if (cell->borderRight() > 0) | 260 if (cell->borderRight() > 0) |
254 cellsWithRightBorder++; | 261 cellsWithRightBorder++; |
255 | 262 |
256 // If the cell has a different color from the table and there is cell spac
ing, | 263 // If the cell has a different color from the table and there is cell |
257 // then it is probably a data table cell (spacing and colors take the plac
e of borders). | 264 // spacing, then it is probably a data table cell (spacing and colors take |
| 265 // the place of borders). |
258 Color cellColor = | 266 Color cellColor = |
259 computedStyle->visitedDependentColor(CSSPropertyBackgroundColor); | 267 computedStyle->visitedDependentColor(CSSPropertyBackgroundColor); |
260 if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0 && | 268 if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0 && |
261 tableBGColor != cellColor && cellColor.alpha() != 1) | 269 tableBGColor != cellColor && cellColor.alpha() != 1) |
262 backgroundDifferenceCellCount++; | 270 backgroundDifferenceCellCount++; |
263 | 271 |
264 // If we've found 10 "good" cells, we don't need to keep searching. | 272 // If we've found 10 "good" cells, we don't need to keep searching. |
265 if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10) | 273 if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10) |
266 return true; | 274 return true; |
267 | 275 |
268 // For the first 5 rows, cache the background color so we can check if thi
s table has zebra-striped rows. | 276 // For the first 5 rows, cache the background color so we can check if |
| 277 // this table has zebra-striped rows. |
269 if (row < 5 && row == alternatingRowColorCount) { | 278 if (row < 5 && row == alternatingRowColorCount) { |
270 LayoutObject* layoutRow = cell->parent(); | 279 LayoutObject* layoutRow = cell->parent(); |
271 if (!layoutRow || !layoutRow->isBoxModelObject() || | 280 if (!layoutRow || !layoutRow->isBoxModelObject() || |
272 !toLayoutBoxModelObject(layoutRow)->isTableRow()) | 281 !toLayoutBoxModelObject(layoutRow)->isTableRow()) |
273 continue; | 282 continue; |
274 const ComputedStyle* rowComputedStyle = layoutRow->style(); | 283 const ComputedStyle* rowComputedStyle = layoutRow->style(); |
275 if (!rowComputedStyle) | 284 if (!rowComputedStyle) |
276 continue; | 285 continue; |
277 Color rowColor = | 286 Color rowColor = |
278 rowComputedStyle->visitedDependentColor(CSSPropertyBackgroundColor); | 287 rowComputedStyle->visitedDependentColor(CSSPropertyBackgroundColor); |
(...skipping 19 matching lines...) Expand all Loading... |
298 cellsWithTopBorder >= neededCellCount || | 307 cellsWithTopBorder >= neededCellCount || |
299 cellsWithBottomBorder >= neededCellCount || | 308 cellsWithBottomBorder >= neededCellCount || |
300 cellsWithLeftBorder >= neededCellCount || | 309 cellsWithLeftBorder >= neededCellCount || |
301 cellsWithRightBorder >= neededCellCount) | 310 cellsWithRightBorder >= neededCellCount) |
302 return true; | 311 return true; |
303 | 312 |
304 // half had different background colors, it's a data table | 313 // half had different background colors, it's a data table |
305 if (backgroundDifferenceCellCount >= neededCellCount) | 314 if (backgroundDifferenceCellCount >= neededCellCount) |
306 return true; | 315 return true; |
307 | 316 |
308 // Check if there is an alternating row background color indicating a zebra st
riped style pattern. | 317 // Check if there is an alternating row background color indicating a zebra |
| 318 // striped style pattern. |
309 if (alternatingRowColorCount > 2) { | 319 if (alternatingRowColorCount > 2) { |
310 Color firstColor = alternatingRowColors[0]; | 320 Color firstColor = alternatingRowColors[0]; |
311 for (int k = 1; k < alternatingRowColorCount; k++) { | 321 for (int k = 1; k < alternatingRowColorCount; k++) { |
312 // If an odd row was the same color as the first row, its not alternating. | 322 // If an odd row was the same color as the first row, its not alternating. |
313 if (k % 2 == 1 && alternatingRowColors[k] == firstColor) | 323 if (k % 2 == 1 && alternatingRowColors[k] == firstColor) |
314 return false; | 324 return false; |
315 // If an even row is not the same as the first row, its not alternating. | 325 // If an even row is not the same as the first row, its not alternating. |
316 if (!(k % 2) && alternatingRowColors[k] != firstColor) | 326 if (!(k % 2) && alternatingRowColors[k] != firstColor) |
317 return false; | 327 return false; |
318 } | 328 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 return; | 381 return; |
372 | 382 |
373 // Add caption | 383 // Add caption |
374 if (HTMLTableCaptionElement* caption = | 384 if (HTMLTableCaptionElement* caption = |
375 toHTMLTableElement(tableNode)->caption()) { | 385 toHTMLTableElement(tableNode)->caption()) { |
376 AXObject* captionObject = axCache.getOrCreate(caption); | 386 AXObject* captionObject = axCache.getOrCreate(caption); |
377 if (captionObject && !captionObject->accessibilityIsIgnored()) | 387 if (captionObject && !captionObject->accessibilityIsIgnored()) |
378 m_children.append(captionObject); | 388 m_children.append(captionObject); |
379 } | 389 } |
380 | 390 |
381 // Go through all the available sections to pull out the rows and add them as
children. | 391 // Go through all the available sections to pull out the rows and add them as |
| 392 // children. |
382 table->recalcSectionsIfNeeded(); | 393 table->recalcSectionsIfNeeded(); |
383 LayoutTableSection* tableSection = table->topSection(); | 394 LayoutTableSection* tableSection = table->topSection(); |
384 if (!tableSection) | 395 if (!tableSection) |
385 return; | 396 return; |
386 | 397 |
387 LayoutTableSection* initialTableSection = tableSection; | 398 LayoutTableSection* initialTableSection = tableSection; |
388 while (tableSection) { | 399 while (tableSection) { |
389 HeapHashSet<Member<AXObject>> appendedRows; | 400 HeapHashSet<Member<AXObject>> appendedRows; |
390 unsigned numRows = tableSection->numRows(); | 401 unsigned numRows = tableSection->numRows(); |
391 for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { | 402 for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 updateChildrenIfNecessary(); | 500 updateChildrenIfNecessary(); |
490 | 501 |
491 return m_rows.size(); | 502 return m_rows.size(); |
492 } | 503 } |
493 | 504 |
494 AXTableCell* AXTable::cellForColumnAndRow(unsigned column, unsigned row) { | 505 AXTableCell* AXTable::cellForColumnAndRow(unsigned column, unsigned row) { |
495 updateChildrenIfNecessary(); | 506 updateChildrenIfNecessary(); |
496 if (column >= columnCount() || row >= rowCount()) | 507 if (column >= columnCount() || row >= rowCount()) |
497 return 0; | 508 return 0; |
498 | 509 |
499 // Iterate backwards through the rows in case the desired cell has a rowspan a
nd exists in a previous row. | 510 // Iterate backwards through the rows in case the desired cell has a rowspan |
| 511 // and exists in a previous row. |
500 for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; | 512 for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; |
501 --rowIndexCounter) { | 513 --rowIndexCounter) { |
502 unsigned rowIndex = rowIndexCounter - 1; | 514 unsigned rowIndex = rowIndexCounter - 1; |
503 const auto& children = m_rows[rowIndex]->children(); | 515 const auto& children = m_rows[rowIndex]->children(); |
504 // Since some cells may have colspans, we have to check the actual range of
each | 516 // Since some cells may have colspans, we have to check the actual range of |
505 // cell to determine which is the right one. | 517 // each cell to determine which is the right one. |
506 for (unsigned colIndexCounter = | 518 for (unsigned colIndexCounter = |
507 std::min(static_cast<unsigned>(children.size()), column + 1); | 519 std::min(static_cast<unsigned>(children.size()), column + 1); |
508 colIndexCounter > 0; --colIndexCounter) { | 520 colIndexCounter > 0; --colIndexCounter) { |
509 unsigned colIndex = colIndexCounter - 1; | 521 unsigned colIndex = colIndexCounter - 1; |
510 AXObject* child = children[colIndex].get(); | 522 AXObject* child = children[colIndex].get(); |
511 | 523 |
512 if (!child->isTableCell()) | 524 if (!child->isTableCell()) |
513 continue; | 525 continue; |
514 | 526 |
515 std::pair<unsigned, unsigned> columnRange; | 527 std::pair<unsigned, unsigned> columnRange; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
550 } | 562 } |
551 | 563 |
552 DEFINE_TRACE(AXTable) { | 564 DEFINE_TRACE(AXTable) { |
553 visitor->trace(m_rows); | 565 visitor->trace(m_rows); |
554 visitor->trace(m_columns); | 566 visitor->trace(m_columns); |
555 visitor->trace(m_headerContainer); | 567 visitor->trace(m_headerContainer); |
556 AXLayoutObject::trace(visitor); | 568 AXLayoutObject::trace(visitor); |
557 } | 569 } |
558 | 570 |
559 } // namespace blink | 571 } // namespace blink |
OLD | NEW |