Migrate accessibility/ to using nullptr instead of 0
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityTableCell.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 "AccessibilityTableCell.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityTable.h"
34 #include "AccessibilityTableRow.h"
35 #include "HTMLElement.h"
36 #include "HTMLNames.h"
37 #include "RenderObject.h"
38 #include "RenderTableCell.h"
39
40 namespace WebCore {
41     
42 using namespace HTMLNames;
43
44 AccessibilityTableCell::AccessibilityTableCell(RenderObject* renderer)
45     : AccessibilityRenderObject(renderer)
46 {
47 }
48
49 AccessibilityTableCell::~AccessibilityTableCell()
50 {
51 }
52
53 PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer)
54 {
55     return adoptRef(new AccessibilityTableCell(renderer));
56 }
57
58 bool AccessibilityTableCell::computeAccessibilityIsIgnored() const
59 {
60     AccessibilityObjectInclusion decision = defaultObjectInclusion();
61     if (decision == IncludeObject)
62         return false;
63     if (decision == IgnoreObject)
64         return true;
65     
66     // Ignore anonymous table cells.
67     if (!node())
68         return true;
69         
70     if (!isTableCell())
71         return AccessibilityRenderObject::computeAccessibilityIsIgnored();
72     
73     return false;
74 }
75
76 AccessibilityTable* AccessibilityTableCell::parentTable() const
77 {
78     if (!m_renderer || !m_renderer->isTableCell())
79         return nullptr;
80
81     // If the document no longer exists, we might not have an axObjectCache.
82     if (!axObjectCache())
83         return nullptr;
84     
85     // Do not use getOrCreate. parentTable() can be called while the render tree is being modified 
86     // by javascript, and creating a table element may try to access the render tree while in a bad state.
87     // By using only get() implies that the AXTable must be created before AXTableCells. This should
88     // always be the case when AT clients access a table.
89     // https://bugs.webkit.org/show_bug.cgi?id=42652    
90     return toAccessibilityTable(axObjectCache()->get(toRenderTableCell(m_renderer)->table()));
91 }
92     
93 bool AccessibilityTableCell::isTableCell() const
94 {
95     AccessibilityObject* parent = parentObjectUnignored();
96     if (!parent || !parent->isTableRow())
97         return false;
98     
99     return true;
100 }
101     
102 AccessibilityRole AccessibilityTableCell::determineAccessibilityRole()
103 {
104     // Always call determineAccessibleRole so that the ARIA role is set.
105     // Even though this object reports a Cell role, the ARIA role will be used
106     // to determine if it's a column header.
107     AccessibilityRole defaultRole = AccessibilityRenderObject::determineAccessibilityRole();
108     if (!isTableCell())
109         return defaultRole;
110     
111     return CellRole;
112 }
113     
114 bool AccessibilityTableCell::isTableHeaderCell() const
115 {
116     return node() && node()->hasTagName(thTag);
117 }
118
119 bool AccessibilityTableCell::isTableCellInSameRowGroup(AccessibilityTableCell* otherTableCell)
120 {
121     Node* parentNode = node();
122     for ( ; parentNode; parentNode = parentNode->parentNode()) {
123         if (parentNode->hasTagName(theadTag) || parentNode->hasTagName(tbodyTag) || parentNode->hasTagName(tfootTag))
124             break;
125     }
126     
127     Node* otherParentNode = otherTableCell->node();
128     for ( ; otherParentNode; otherParentNode = otherParentNode->parentNode()) {
129         if (otherParentNode->hasTagName(theadTag) || otherParentNode->hasTagName(tbodyTag) || otherParentNode->hasTagName(tfootTag))
130             break;
131     }
132     
133     return otherParentNode == parentNode;
134 }
135
136
137 bool AccessibilityTableCell::isTableCellInSameColGroup(AccessibilityTableCell* tableCell)
138 {
139     std::pair<unsigned, unsigned> colRange;
140     columnIndexRange(colRange);
141     
142     std::pair<unsigned, unsigned> otherColRange;
143     tableCell->columnIndexRange(otherColRange);
144     
145     if (colRange.first <= (otherColRange.first + otherColRange.second))
146         return true;
147     return false;
148 }
149     
150 String AccessibilityTableCell::expandedTextValue() const
151 {
152     return getAttribute(abbrAttr);
153 }
154     
155 bool AccessibilityTableCell::supportsExpandedTextValue() const
156 {
157     return isTableHeaderCell() && hasAttribute(abbrAttr);
158 }
159     
160 void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers)
161 {
162     AccessibilityTable* parent = parentTable();
163     if (!parent)
164         return;
165
166     // Choose columnHeaders as the place where the "headers" attribute is reported.
167     ariaElementsFromAttribute(headers, headersAttr);
168     // If the headers attribute returned valid values, then do not further search for column headers.
169     if (!headers.isEmpty())
170         return;
171     
172     std::pair<unsigned, unsigned> rowRange;
173     rowIndexRange(rowRange);
174     
175     std::pair<unsigned, unsigned> colRange;
176     columnIndexRange(colRange);
177     
178     for (unsigned row = 0; row < rowRange.first; row++) {
179         AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row);
180         if (!tableCell || tableCell == this || headers.contains(tableCell))
181             continue;
182
183         std::pair<unsigned, unsigned> childRowRange;
184         tableCell->rowIndexRange(childRowRange);
185             
186         const AtomicString& scope = tableCell->getAttribute(scopeAttr);
187         if (scope == "col" || tableCell->isTableHeaderCell())
188             headers.append(tableCell);
189         else if (scope == "colgroup" && isTableCellInSameColGroup(tableCell))
190             headers.append(tableCell);
191     }
192 }
193     
194 void AccessibilityTableCell::rowHeaders(AccessibilityChildrenVector& headers)
195 {
196     AccessibilityTable* parent = parentTable();
197     if (!parent)
198         return;
199
200     std::pair<unsigned, unsigned> rowRange;
201     rowIndexRange(rowRange);
202
203     std::pair<unsigned, unsigned> colRange;
204     columnIndexRange(colRange);
205
206     for (unsigned column = 0; column < colRange.first; column++) {
207         AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(column, rowRange.first);
208         if (!tableCell || tableCell == this || headers.contains(tableCell))
209             continue;
210         
211         const AtomicString& scope = tableCell->getAttribute(scopeAttr);
212         if (scope == "row")
213             headers.append(tableCell);
214         else if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell))
215             headers.append(tableCell);
216     }
217 }
218     
219 void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange)
220 {
221     if (!m_renderer || !m_renderer->isTableCell())
222         return;
223     
224     RenderTableCell* renderCell = toRenderTableCell(m_renderer);
225     rowRange.first = renderCell->rowIndex();
226     rowRange.second = renderCell->rowSpan();
227     
228     // since our table might have multiple sections, we have to offset our row appropriately
229     RenderTableSection* section = renderCell->section();
230     RenderTable* table = renderCell->table();
231     if (!table || !section)
232         return;
233
234     RenderTableSection* footerSection = table->footer();
235     unsigned rowOffset = 0;
236     for (RenderTableSection* tableSection = table->topSection(); tableSection; tableSection = table->sectionBelow(tableSection, SkipEmptySections)) {
237         // Don't add row offsets for bottom sections that are placed in before the body section.
238         if (tableSection == footerSection)
239             continue;
240         if (tableSection == section)
241             break;
242         rowOffset += tableSection->numRows();
243     }
244
245     rowRange.first += rowOffset;
246 }
247     
248 void AccessibilityTableCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange)
249 {
250     if (!m_renderer || !m_renderer->isTableCell())
251         return;
252     
253     const RenderTableCell& cell = *toRenderTableCell(m_renderer);
254     columnRange.first = cell.table()->colToEffCol(cell.col());
255     columnRange.second = cell.table()->colToEffCol(cell.col() + cell.colSpan()) - columnRange.first;
256 }
257     
258 AccessibilityObject* AccessibilityTableCell::titleUIElement() const
259 {
260     // Try to find if the first cell in this row is a <th>. If it is,
261     // then it can act as the title ui element. (This is only in the
262     // case when the table is not appearing as an AXTable.)
263     if (isTableCell() || !m_renderer || !m_renderer->isTableCell())
264         return nullptr;
265
266     // Table cells that are th cannot have title ui elements, since by definition
267     // they are title ui elements
268     Node* node = m_renderer->node();
269     if (node && node->hasTagName(thTag))
270         return nullptr;
271     
272     RenderTableCell* renderCell = toRenderTableCell(m_renderer);
273
274     // If this cell is in the first column, there is no need to continue.
275     int col = renderCell->col();
276     if (!col)
277         return nullptr;
278
279     int row = renderCell->rowIndex();
280
281     RenderTableSection* section = renderCell->section();
282     if (!section)
283         return nullptr;
284     
285     RenderTableCell* headerCell = section->primaryCellAt(row, 0);
286     if (!headerCell || headerCell == renderCell)
287         return nullptr;
288
289     if (!headerCell->element() || !headerCell->element()->hasTagName(thTag))
290         return nullptr;
291     
292     return axObjectCache()->getOrCreate(headerCell);
293 }
294     
295 } // namespace WebCore