Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityTable.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "AccessibilityTable.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityTableCell.h"
34 #include "AccessibilityTableColumn.h"
35 #include "AccessibilityTableHeaderContainer.h"
36 #include "AccessibilityTableRow.h"
37 #include "ElementIterator.h"
38 #include "HTMLNames.h"
39 #include "HTMLTableCaptionElement.h"
40 #include "HTMLTableCellElement.h"
41 #include "HTMLTableElement.h"
42 #include "HTMLTableSectionElement.h"
43 #include "RenderObject.h"
44 #include "RenderTable.h"
45 #include "RenderTableCell.h"
46 #include "RenderTableSection.h"
47
48 #include <wtf/Deque.h>
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 AccessibilityTable::AccessibilityTable(RenderObject* renderer)
55     : AccessibilityRenderObject(renderer)
56     , m_headerContainer(nullptr)
57     , m_isExposableThroughAccessibility(true)
58 {
59 }
60
61 AccessibilityTable::~AccessibilityTable() = default;
62
63 void AccessibilityTable::init()
64 {
65     AccessibilityRenderObject::init();
66     m_isExposableThroughAccessibility = computeIsTableExposableThroughAccessibility();
67 }
68
69 Ref<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
70 {
71     return adoptRef(*new AccessibilityTable(renderer));
72 }
73
74 bool AccessibilityTable::hasARIARole() const
75 {
76     if (!m_renderer)
77         return false;
78     
79     AccessibilityRole ariaRole = ariaRoleAttribute();
80     if (ariaRole != AccessibilityRole::Unknown)
81         return true;
82
83     return false;
84 }
85
86 bool AccessibilityTable::isExposableThroughAccessibility() const
87 {
88     if (!m_renderer)
89         return false;
90     
91     return m_isExposableThroughAccessibility;
92 }
93
94 HTMLTableElement* AccessibilityTable::tableElement() const
95 {
96     if (!is<RenderTable>(*m_renderer))
97         return nullptr;
98     
99     RenderTable& table = downcast<RenderTable>(*m_renderer);
100     if (is<HTMLTableElement>(table.element()))
101         return downcast<HTMLTableElement>(table.element());
102     // Try to find the table element, when the AccessibilityTable is mapped to an anonymous table renderer.
103     auto* firstChild = table.firstChild();
104     if (!firstChild || !firstChild->node())
105         return nullptr;
106     if (is<HTMLTableElement>(*firstChild->node()))
107         return downcast<HTMLTableElement>(firstChild->node());
108     // FIXME: This might find an unrelated parent table element.
109     return ancestorsOfType<HTMLTableElement>(*(firstChild->node())).first();
110 }
111     
112 bool AccessibilityTable::isDataTable() const
113 {
114     if (!m_renderer)
115         return false;
116
117     // Do not consider it a data table is it has an ARIA role.
118     if (hasARIARole())
119         return false;
120
121     // When a section of the document is contentEditable, all tables should be
122     // treated as data tables, otherwise users may not be able to work with rich
123     // text editors that allow creating and editing tables.
124     if (node() && node()->hasEditableStyle())
125         return true;
126
127     if (!is<RenderTable>(*m_renderer))
128         return false;
129
130     // This employs a heuristic to determine if this table should appear.
131     // Only "data" tables should be exposed as tables.
132     // Unfortunately, there is no good way to determine the difference
133     // between a "layout" table and a "data" table.
134     if (HTMLTableElement* tableElement = this->tableElement()) {
135         // If there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table.
136         if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
137             return true;
138         
139         // If someone used "rules" attribute than the table should appear.
140         if (!tableElement->rules().isEmpty())
141             return true;
142
143         // If there's a colgroup or col element, it's probably a data table.
144         for (const auto& child : childrenOfType<HTMLElement>(*tableElement)) {
145             if (child.hasTagName(colTag) || child.hasTagName(colgroupTag))
146                 return true;
147         }
148     }
149     
150     // The following checks should only apply if this is a real <table> element.
151     if (!hasTagName(tableTag))
152         return false;
153     
154     // If the author has used ARIA to specify a valid column or row count, assume they
155     // want us to treat the table as a data table.
156     int axColumnCount = getAttribute(aria_colcountAttr).toInt();
157     if (axColumnCount == -1 || axColumnCount > 0)
158         return true;
159
160     int axRowCount = getAttribute(aria_rowcountAttr).toInt();
161     if (axRowCount == -1 || axRowCount > 0)
162         return true;
163
164     RenderTable& table = downcast<RenderTable>(*m_renderer);
165     // go through the cell's and check for tell-tale signs of "data" table status
166     // cells have borders, or use attributes like headers, abbr, scope or axis
167     table.recalcSectionsIfNeeded();
168     RenderTableSection* firstBody = table.firstBody();
169     if (!firstBody)
170         return false;
171     
172     int numCols = firstBody->numColumns();
173     int numRows = firstBody->numRows();
174     
175     // If there are at least 20 rows, we'll call it a data table.
176     if (numRows >= 20)
177         return true;
178     
179     // Store the background color of the table to check against cell's background colors.
180     const RenderStyle& tableStyle = table.style();
181     Color tableBGColor = tableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
182     
183     // check enough of the cells to find if the table matches our criteria
184     // Criteria: 
185     //   1) must have at least one valid cell (and)
186     //   2) at least half of cells have borders (or)
187     //   3) at least half of cells have different bg colors than the table, and there is cell spacing (or)
188     //   4) the valid cell has an ARIA cell-related property
189     unsigned validCellCount = 0;
190     unsigned borderedCellCount = 0;
191     unsigned backgroundDifferenceCellCount = 0;
192     unsigned cellsWithTopBorder = 0;
193     unsigned cellsWithBottomBorder = 0;
194     unsigned cellsWithLeftBorder = 0;
195     unsigned cellsWithRightBorder = 0;
196     
197     Color alternatingRowColors[5];
198     int alternatingRowColorCount = 0;
199     
200     int headersInFirstColumnCount = 0;
201     for (int row = 0; row < numRows; ++row) {
202     
203         int headersInFirstRowCount = 0;
204         for (int col = 0; col < numCols; ++col) {    
205             RenderTableCell* cell = firstBody->primaryCellAt(row, col);
206             if (!cell)
207                 continue;
208
209             Element* cellElement = cell->element();
210             if (!cellElement)
211                 continue;
212             
213             if (cell->width() < 1 || cell->height() < 1)
214                 continue;
215             
216             ++validCellCount;
217             
218             bool isTHCell = cellElement->hasTagName(thTag);
219             // If the first row is comprised of all <th> tags, assume it is a data table.
220             if (!row && isTHCell)
221                 ++headersInFirstRowCount;
222
223             // If the first column is comprised of all <th> tags, assume it is a data table.
224             if (!col && isTHCell)
225                 ++headersInFirstColumnCount;
226             
227             // In this case, the developer explicitly assigned a "data" table attribute.
228             if (is<HTMLTableCellElement>(*cellElement)) {
229                 HTMLTableCellElement& tableCellElement = downcast<HTMLTableCellElement>(*cellElement);
230                 if (!tableCellElement.headers().isEmpty() || !tableCellElement.abbr().isEmpty()
231                     || !tableCellElement.axis().isEmpty() || !tableCellElement.scope().isEmpty())
232                     return true;
233             }
234
235             // If the author has used ARIA to specify a valid column or row index, assume they want us
236             // to treat the table as a data table.
237             int axColumnIndex =  cellElement->attributeWithoutSynchronization(aria_colindexAttr).toInt();
238             if (axColumnIndex >= 1)
239                 return true;
240
241             int axRowIndex = cellElement->attributeWithoutSynchronization(aria_rowindexAttr).toInt();
242             if (axRowIndex >= 1)
243                 return true;
244
245             if (auto cellParentElement = cellElement->parentElement()) {
246                 axRowIndex = cellParentElement->attributeWithoutSynchronization(aria_rowindexAttr).toInt();
247                 if (axRowIndex >= 1)
248                     return true;
249             }
250
251             // If the author has used ARIA to specify a column or row span, we're supposed to ignore
252             // the value for the purposes of exposing the span. But assume they want us to treat the
253             // table as a data table.
254             int axColumnSpan = cellElement->attributeWithoutSynchronization(aria_colspanAttr).toInt();
255             if (axColumnSpan >= 1)
256                 return true;
257
258             int axRowSpan = cellElement->attributeWithoutSynchronization(aria_rowspanAttr).toInt();
259             if (axRowSpan >= 1)
260                 return true;
261
262             const RenderStyle& renderStyle = cell->style();
263
264             // If the empty-cells style is set, we'll call it a data table.
265             if (renderStyle.emptyCells() == EmptyCell::Hide)
266                 return true;
267
268             // If a cell has matching bordered sides, call it a (fully) bordered cell.
269             if ((cell->borderTop() > 0 && cell->borderBottom() > 0)
270                 || (cell->borderLeft() > 0 && cell->borderRight() > 0))
271                 ++borderedCellCount;
272
273             // Also keep track of each individual border, so we can catch tables where most
274             // cells have a bottom border, for example.
275             if (cell->borderTop() > 0)
276                 ++cellsWithTopBorder;
277             if (cell->borderBottom() > 0)
278                 ++cellsWithBottomBorder;
279             if (cell->borderLeft() > 0)
280                 ++cellsWithLeftBorder;
281             if (cell->borderRight() > 0)
282                 ++cellsWithRightBorder;
283             
284             // If the cell has a different color from the table and there is cell spacing,
285             // then it is probably a data table cell (spacing and colors take the place of borders).
286             Color cellColor = renderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
287             if (table.hBorderSpacing() > 0 && table.vBorderSpacing() > 0
288                 && tableBGColor != cellColor && cellColor.alpha() != 1)
289                 ++backgroundDifferenceCellCount;
290             
291             // If we've found 10 "good" cells, we don't need to keep searching.
292             if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10)
293                 return true;
294             
295             // For the first 5 rows, cache the background color so we can check if this table has zebra-striped rows.
296             if (row < 5 && row == alternatingRowColorCount) {
297                 RenderElement* renderRow = cell->parent();
298                 if (!is<RenderTableRow>(renderRow))
299                     continue;
300                 const RenderStyle& rowRenderStyle = renderRow->style();
301                 Color rowColor = rowRenderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
302                 alternatingRowColors[alternatingRowColorCount] = rowColor;
303                 ++alternatingRowColorCount;
304             }
305         }
306         
307         if (!row && headersInFirstRowCount == numCols && numCols > 1)
308             return true;
309     }
310
311     if (headersInFirstColumnCount == numRows && numRows > 1)
312         return true;
313     
314     // if there is less than two valid cells, it's not a data table
315     if (validCellCount <= 1)
316         return false;
317     
318     // half of the cells had borders, it's a data table
319     unsigned neededCellCount = validCellCount / 2;
320     if (borderedCellCount >= neededCellCount
321         || cellsWithTopBorder >= neededCellCount
322         || cellsWithBottomBorder >= neededCellCount
323         || cellsWithLeftBorder >= neededCellCount
324         || cellsWithRightBorder >= neededCellCount)
325         return true;
326     
327     // half had different background colors, it's a data table
328     if (backgroundDifferenceCellCount >= neededCellCount)
329         return true;
330
331     // Check if there is an alternating row background color indicating a zebra striped style pattern.
332     if (alternatingRowColorCount > 2) {
333         Color firstColor = alternatingRowColors[0];
334         for (int k = 1; k < alternatingRowColorCount; k++) {
335             // If an odd row was the same color as the first row, its not alternating.
336             if (k % 2 == 1 && alternatingRowColors[k] == firstColor)
337                 return false;
338             // If an even row is not the same as the first row, its not alternating.
339             if (!(k % 2) && alternatingRowColors[k] != firstColor)
340                 return false;
341         }
342         return true;
343     }
344     
345     return false;
346 }
347     
348 bool AccessibilityTable::computeIsTableExposableThroughAccessibility() const
349 {
350     // The following is a heuristic used to determine if a
351     // <table> should be exposed as an AXTable. The goal
352     // is to only show "data" tables.
353
354     if (!m_renderer)
355         return false;
356
357     // If the developer assigned an aria role to this, then we
358     // shouldn't expose it as a table, unless, of course, the aria
359     // role is a table.
360     if (hasARIARole())
361         return false;
362
363     return isDataTable();
364 }
365
366 void AccessibilityTable::clearChildren()
367 {
368     AccessibilityRenderObject::clearChildren();
369     m_rows.clear();
370     m_columns.clear();
371
372     if (m_headerContainer) {
373         m_headerContainer->detachFromParent();
374         m_headerContainer = nullptr;
375     }
376 }
377
378 void AccessibilityTable::addChildren()
379 {
380     if (!isExposableThroughAccessibility()) {
381         AccessibilityRenderObject::addChildren();
382         return;
383     }
384     
385     ASSERT(!m_haveChildren); 
386     
387     m_haveChildren = true;
388     if (!is<RenderTable>(renderer()))
389         return;
390     
391     RenderTable& table = downcast<RenderTable>(*m_renderer);
392     // Go through all the available sections to pull out the rows and add them as children.
393     table.recalcSectionsIfNeeded();
394     
395     if (HTMLTableElement* tableElement = this->tableElement()) {
396         if (auto caption = tableElement->caption()) {
397             AccessibilityObject* axCaption = axObjectCache()->getOrCreate(caption.get());
398             if (axCaption && !axCaption->accessibilityIsIgnored())
399                 m_children.append(axCaption);
400         }
401     }
402
403     unsigned maxColumnCount = 0;
404     RenderTableSection* footer = table.footer();
405     
406     for (RenderTableSection* tableSection = table.topSection(); tableSection; tableSection = table.sectionBelow(tableSection, SkipEmptySections)) {
407         if (tableSection == footer)
408             continue;
409         addChildrenFromSection(tableSection, maxColumnCount);
410     }
411     
412     // Process the footer last, in case it was ordered earlier in the DOM.
413     if (footer)
414         addChildrenFromSection(footer, maxColumnCount);
415     
416     AXObjectCache* axCache = m_renderer->document().axObjectCache();
417     // make the columns based on the number of columns in the first body
418     unsigned length = maxColumnCount;
419     for (unsigned i = 0; i < length; ++i) {
420         auto& column = downcast<AccessibilityTableColumn>(*axCache->getOrCreate(AccessibilityRole::Column));
421         column.setColumnIndex((int)i);
422         column.setParent(this);
423         m_columns.append(&column);
424         if (!column.accessibilityIsIgnored())
425             m_children.append(&column);
426     }
427     
428     AccessibilityObject* headerContainerObject = headerContainer();
429     if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
430         m_children.append(headerContainerObject);
431
432     // Sometimes the cell gets the wrong role initially because it is created before the parent
433     // determines whether it is an accessibility table. Iterate all the cells and allow them to
434     // update their roles now that the table knows its status.
435     // see bug: https://bugs.webkit.org/show_bug.cgi?id=147001
436     for (const auto& row : m_rows) {
437         for (const auto& cell : row->children())
438             cell->updateAccessibilityRole();
439     }
440
441 }
442
443 void AccessibilityTable::addTableCellChild(AccessibilityObject* rowObject, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
444 {
445     if (!rowObject || !is<AccessibilityTableRow>(*rowObject))
446         return;
447
448     auto& row = downcast<AccessibilityTableRow>(*rowObject);
449     // We need to check every cell for a new row, because cell spans
450     // can cause us to miss rows if we just check the first column.
451     if (appendedRows.contains(&row))
452         return;
453     
454     row.setRowIndex(static_cast<int>(m_rows.size()));
455     m_rows.append(&row);
456     if (!row.accessibilityIsIgnored())
457         m_children.append(&row);
458     appendedRows.add(&row);
459         
460     // store the maximum number of columns
461     unsigned rowCellCount = row.children().size();
462     if (rowCellCount > columnCount)
463         columnCount = rowCellCount;
464 }
465
466 void AccessibilityTable::addChildrenFromSection(RenderTableSection* tableSection, unsigned& maxColumnCount)
467 {
468     ASSERT(tableSection);
469     if (!tableSection)
470         return;
471     
472     AXObjectCache* axCache = m_renderer->document().axObjectCache();
473     HashSet<AccessibilityObject*> appendedRows;
474     unsigned numRows = tableSection->numRows();
475     for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
476         
477         RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex);
478         if (!renderRow)
479             continue;
480         
481         AccessibilityObject& rowObject = *axCache->getOrCreate(renderRow);
482         
483         // If the row is anonymous, we should dive deeper into the descendants to try to find a valid row.
484         if (renderRow->isAnonymous()) {
485             Deque<AccessibilityObject*> queue;
486             queue.append(&rowObject);
487             
488             while (!queue.isEmpty()) {
489                 AccessibilityObject* obj = queue.takeFirst();
490                 if (obj->node() && is<AccessibilityTableRow>(*obj)) {
491                     addTableCellChild(obj, appendedRows, maxColumnCount);
492                     continue;
493                 }
494                 for (auto* child = obj->firstChild(); child; child = child->nextSibling())
495                     queue.append(child);
496             }
497         } else
498             addTableCellChild(&rowObject, appendedRows, maxColumnCount);
499     }
500     
501     maxColumnCount = std::max(tableSection->numColumns(), maxColumnCount);
502 }
503     
504 AccessibilityObject* AccessibilityTable::headerContainer()
505 {
506     if (m_headerContainer)
507         return m_headerContainer.get();
508     
509     auto& tableHeader = downcast<AccessibilityMockObject>(*axObjectCache()->getOrCreate(AccessibilityRole::TableHeaderContainer));
510     tableHeader.setParent(this);
511
512     m_headerContainer = &tableHeader;
513     return m_headerContainer.get();
514 }
515
516 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::columns()
517 {
518     updateChildrenIfNecessary();
519         
520     return m_columns;
521 }
522
523 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows()
524 {
525     updateChildrenIfNecessary();
526     
527     return m_rows;
528 }
529
530 void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
531 {
532     if (!m_renderer)
533         return;
534     
535     updateChildrenIfNecessary();
536     
537     // Sometimes m_columns can be reset during the iteration, we cache it here to be safe.
538     AccessibilityChildrenVector columnsCopy = m_columns;
539     
540     for (const auto& column : columnsCopy) {
541         if (AccessibilityObject* header = downcast<AccessibilityTableColumn>(*column).headerObject())
542             headers.append(header);
543     }
544 }
545
546 void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
547 {
548     if (!m_renderer)
549         return;
550     
551     updateChildrenIfNecessary();
552     
553     // Sometimes m_rows can be reset during the iteration, we cache it here to be safe.
554     AccessibilityChildrenVector rowsCopy = m_rows;
555     
556     for (const auto& row : rowsCopy) {
557         if (AccessibilityObject* header = downcast<AccessibilityTableRow>(*row).headerObject())
558             headers.append(header);
559     }
560 }
561
562 void AccessibilityTable::visibleRows(AccessibilityChildrenVector& rows)
563 {
564     if (!m_renderer)
565         return;
566     
567     updateChildrenIfNecessary();
568     
569     for (const auto& row : m_rows) {
570         if (row && !row->isOffScreen())
571             rows.append(row);
572     }
573 }
574
575 void AccessibilityTable::cells(AccessibilityObject::AccessibilityChildrenVector& cells)
576 {
577     if (!m_renderer)
578         return;
579     
580     updateChildrenIfNecessary();
581     
582     for (const auto& row : m_rows)
583         cells.appendVector(row->children());
584 }
585     
586 unsigned AccessibilityTable::columnCount()
587 {
588     updateChildrenIfNecessary();
589     
590     return m_columns.size();    
591 }
592     
593 unsigned AccessibilityTable::rowCount()
594 {
595     updateChildrenIfNecessary();
596     
597     return m_rows.size();
598 }
599
600 int AccessibilityTable::tableLevel() const
601 {
602     int level = 0;
603     for (AccessibilityObject* obj = static_cast<AccessibilityObject*>(const_cast<AccessibilityTable*>(this)); obj; obj = obj->parentObject()) {
604         if (is<AccessibilityTable>(*obj) && downcast<AccessibilityTable>(*obj).isExposableThroughAccessibility())
605             ++level;
606     }
607     
608     return level;
609 }
610
611 AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row)
612 {
613     updateChildrenIfNecessary();
614     if (column >= columnCount() || row >= rowCount())
615         return nullptr;
616     
617     // Iterate backwards through the rows in case the desired cell has a rowspan and exists in a previous row.
618     for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; --rowIndexCounter) {
619         unsigned rowIndex = rowIndexCounter - 1;
620         const auto& children = m_rows[rowIndex]->children();
621         // Since some cells may have colspans, we have to check the actual range of each
622         // cell to determine which is the right one.
623         for (unsigned colIndexCounter = std::min(static_cast<unsigned>(children.size()), column + 1); colIndexCounter > 0; --colIndexCounter) {
624             unsigned colIndex = colIndexCounter - 1;
625             AccessibilityObject* child = children[colIndex].get();
626             ASSERT(is<AccessibilityTableCell>(*child));
627             if (!is<AccessibilityTableCell>(*child))
628                 continue;
629             
630             std::pair<unsigned, unsigned> columnRange;
631             std::pair<unsigned, unsigned> rowRange;
632             auto& tableCellChild = downcast<AccessibilityTableCell>(*child);
633             tableCellChild.columnIndexRange(columnRange);
634             tableCellChild.rowIndexRange(rowRange);
635             
636             if ((column >= columnRange.first && column < (columnRange.first + columnRange.second))
637                 && (row >= rowRange.first && row < (rowRange.first + rowRange.second)))
638                 return &tableCellChild;
639         }
640     }
641     
642     return nullptr;
643 }
644
645 AccessibilityRole AccessibilityTable::roleValue() const
646 {
647     if (!isExposableThroughAccessibility())
648         return AccessibilityRenderObject::roleValue();
649     
650     AccessibilityRole ariaRole = ariaRoleAttribute();
651     if (ariaRole == AccessibilityRole::Grid || ariaRole == AccessibilityRole::TreeGrid)
652         return ariaRole;
653
654     return AccessibilityRole::Table;
655 }
656     
657 bool AccessibilityTable::computeAccessibilityIsIgnored() const
658 {
659     AccessibilityObjectInclusion decision = defaultObjectInclusion();
660     if (decision == AccessibilityObjectInclusion::IncludeObject)
661         return false;
662     if (decision == AccessibilityObjectInclusion::IgnoreObject)
663         return true;
664     
665     if (!isExposableThroughAccessibility())
666         return AccessibilityRenderObject::computeAccessibilityIsIgnored();
667         
668     return false;
669 }
670
671 void AccessibilityTable::titleElementText(Vector<AccessibilityText>& textOrder) const
672 {
673     String title = this->title();
674     if (!title.isEmpty())
675         textOrder.append(AccessibilityText(title, AccessibilityTextSource::LabelByElement));
676 }
677
678 String AccessibilityTable::title() const
679 {
680     if (!isExposableThroughAccessibility())
681         return AccessibilityRenderObject::title();
682     
683     String title;
684     if (!m_renderer)
685         return title;
686     
687     // see if there is a caption
688     Node* tableElement = m_renderer->node();
689     if (is<HTMLTableElement>(tableElement)) {
690         if (auto caption = downcast<HTMLTableElement>(*tableElement).caption())
691             title = caption->innerText();
692     }
693     
694     // try the standard 
695     if (title.isEmpty())
696         title = AccessibilityRenderObject::title();
697     
698     return title;
699 }
700
701 int AccessibilityTable::axColumnCount() const
702 {
703     const AtomString& colCountValue = getAttribute(aria_colcountAttr);
704     int colCountInt = colCountValue.toInt();
705     // The ARIA spec states, "Authors must set the value of aria-colcount to an integer equal to the
706     // number of columns in the full table. If the total number of columns is unknown, authors must
707     // set the value of aria-colcount to -1 to indicate that the value should not be calculated by
708     // the user agent." If we have a valid value, make it available to platforms.
709     if (colCountInt == -1 || colCountInt >= (int)m_columns.size())
710         return colCountInt;
711     
712     return 0;
713 }
714
715 int AccessibilityTable::axRowCount() const
716 {
717     const AtomString& rowCountValue = getAttribute(aria_rowcountAttr);
718     int rowCountInt = rowCountValue.toInt();
719     // The ARIA spec states, "Authors must set the value of aria-rowcount to an integer equal to the
720     // number of rows in the full table. If the total number of rows is unknown, authors must set
721     // the value of aria-rowcount to -1 to indicate that the value should not be calculated by the
722     // user agent." If we have a valid value, make it available to platforms.
723     if (rowCountInt == -1 || rowCountInt >= (int)m_rows.size())
724         return rowCountInt;
725     
726     return 0;
727 }
728
729 } // namespace WebCore