3fa78b5582775391f749e6a6dfbd44806370bdee
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityTableColumn.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 "AccessibilityTableColumn.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityTableCell.h"
34 #include "HTMLElement.h"
35 #include "HTMLNames.h"
36 #include "RenderTable.h"
37 #include "RenderTableCell.h"
38 #include "RenderTableSection.h"
39
40 namespace WebCore {
41     
42 using namespace HTMLNames;
43
44 AccessibilityTableColumn::AccessibilityTableColumn()
45 {
46 }
47
48 AccessibilityTableColumn::~AccessibilityTableColumn()
49 {
50 }    
51
52 Ref<AccessibilityTableColumn> AccessibilityTableColumn::create()
53 {
54     return adoptRef(*new AccessibilityTableColumn());
55 }
56
57 void AccessibilityTableColumn::setParent(AccessibilityObject* parent)
58 {
59     AccessibilityMockObject::setParent(parent);
60     
61     clearChildren();
62 }
63     
64 LayoutRect AccessibilityTableColumn::elementRect() const
65 {
66     // This used to be cached during the call to addChildren(), but calling elementRect()
67     // can invalidate elements, so its better to ask for this on demand.
68     LayoutRect columnRect;
69     AccessibilityChildrenVector childrenCopy = m_children;
70     for (const auto& cell : childrenCopy)
71         columnRect.unite(cell->elementRect());
72
73     return columnRect;
74 }
75
76 AccessibilityObject* AccessibilityTableColumn::headerObject()
77 {
78     if (!m_parent)
79         return nullptr;
80     
81     RenderObject* renderer = m_parent->renderer();
82     if (!renderer)
83         return nullptr;
84     if (!is<AccessibilityTable>(*m_parent))
85         return nullptr;
86
87     auto& parentTable = downcast<AccessibilityTable>(*m_parent);
88     if (!parentTable.isExposableThroughAccessibility())
89         return nullptr;
90     
91     if (parentTable.isAriaTable()) {
92         for (const auto& cell : children()) {
93             if (cell->ariaRoleAttribute() == ColumnHeaderRole)
94                 return cell.get();
95         }
96         
97         return nullptr;
98     }
99
100     if (!is<RenderTable>(*renderer))
101         return nullptr;
102     
103     RenderTable& table = downcast<RenderTable>(*renderer);
104
105     // try the <thead> section first. this doesn't require th tags
106     if (auto* headerObject = headerObjectForSection(table.header(), false))
107         return headerObject;
108     
109     RenderTableSection* bodySection = table.firstBody();
110     while (bodySection && bodySection->isAnonymous())
111         bodySection = table.sectionBelow(bodySection, SkipEmptySections);
112     
113     // now try for <th> tags in the first body. If the first body is 
114     return headerObjectForSection(bodySection, true);
115 }
116
117 AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired)
118 {
119     if (!section)
120         return nullptr;
121     
122     unsigned numCols = section->numColumns();
123     if (m_columnIndex >= numCols)
124         return nullptr;
125     
126     if (!section->numRows())
127         return nullptr;
128     
129     RenderTableCell* cell = nullptr;
130     // also account for cells that have a span
131     for (int testCol = m_columnIndex; testCol >= 0; --testCol) {
132         
133         // Run down the rows in case initial rows are invalid (like when a <caption> is used).
134         unsigned rowCount = section->numRows();
135         for (unsigned testRow = 0; testRow < rowCount; testRow++) {
136             RenderTableCell* testCell = section->primaryCellAt(testRow, testCol);
137             // No cell at this index, keep checking more rows and columns.
138             if (!testCell)
139                 continue;
140             
141             // If we've reached a cell that doesn't even overlap our column it can't be the header.
142             if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex)
143                 break;
144             
145             // If this does not have an element (like a <caption>) then check the next row
146             if (!testCell->element())
147                 continue;
148             
149             // If th is required, but we found an element that doesn't have a th tag, we can stop looking.
150             if (thTagRequired && !testCell->element()->hasTagName(thTag))
151                 break;
152             
153             cell = testCell;
154             break;
155         }
156     }
157     
158     if (!cell)
159         return nullptr;
160
161     return axObjectCache()->getOrCreate(cell);
162 }
163     
164 bool AccessibilityTableColumn::computeAccessibilityIsIgnored() const
165 {
166     if (!m_parent)
167         return true;
168     
169 #if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(EFL)
170     return true;
171 #endif
172     
173     return m_parent->accessibilityIsIgnored();
174 }
175     
176 void AccessibilityTableColumn::addChildren()
177 {
178     ASSERT(!m_haveChildren); 
179     
180     m_haveChildren = true;
181     if (!is<AccessibilityTable>(m_parent))
182         return;
183
184     auto& parentTable = downcast<AccessibilityTable>(*m_parent);
185     if (!parentTable.isExposableThroughAccessibility())
186         return;
187     
188     int numRows = parentTable.rowCount();
189     
190     for (int i = 0; i < numRows; ++i) {
191         AccessibilityTableCell* cell = parentTable.cellForColumnAndRow(m_columnIndex, i);
192         if (!cell)
193             continue;
194         
195         // make sure the last one isn't the same as this one (rowspan cells)
196         if (m_children.size() > 0 && m_children.last() == cell)
197             continue;
198             
199         m_children.append(cell);
200     }
201 }
202     
203 } // namespace WebCore