1 /*
2 * Copyright (C) 2009 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 Computer, 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 "AccessibilityARIAGrid.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityTableCell.h"
34 #include "AccessibilityTableColumn.h"
35 #include "AccessibilityTableHeaderContainer.h"
36 #include "AccessibilityTableRow.h"
37 #include "RenderObject.h"
38
39 using namespace std;
40
41 namespace WebCore {
42
AccessibilityARIAGrid(RenderObject * renderer)43 AccessibilityARIAGrid::AccessibilityARIAGrid(RenderObject* renderer)
44 : AccessibilityTable(renderer)
45 {
46 #if ACCESSIBILITY_TABLES
47 m_isAccessibilityTable = true;
48 #else
49 m_isAccessibilityTable = false;
50 #endif
51 }
52
~AccessibilityARIAGrid()53 AccessibilityARIAGrid::~AccessibilityARIAGrid()
54 {
55 }
56
create(RenderObject * renderer)57 PassRefPtr<AccessibilityARIAGrid> AccessibilityARIAGrid::create(RenderObject* renderer)
58 {
59 return adoptRef(new AccessibilityARIAGrid(renderer));
60 }
61
addChild(AccessibilityObject * child,HashSet<AccessibilityObject * > & appendedRows,unsigned & columnCount)62 void AccessibilityARIAGrid::addChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
63 {
64 if (!child || !child->isTableRow() || child->ariaRoleAttribute() != RowRole)
65 return;
66
67 AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(child);
68 if (appendedRows.contains(row))
69 return;
70
71 // store the maximum number of columns
72 unsigned rowCellCount = row->children().size();
73 if (rowCellCount > columnCount)
74 columnCount = rowCellCount;
75
76 row->setRowIndex((int)m_rows.size());
77 m_rows.append(row);
78
79 // Try adding the row if it's not ignoring accessibility,
80 // otherwise add its children (the cells) as the grid's children.
81 if (!row->accessibilityIsIgnored())
82 m_children.append(row);
83 else
84 m_children.append(row->children());
85
86 appendedRows.add(row);
87 }
88
addChildren()89 void AccessibilityARIAGrid::addChildren()
90 {
91 ASSERT(!m_haveChildren);
92
93 if (!isAccessibilityTable()) {
94 AccessibilityRenderObject::addChildren();
95 return;
96 }
97
98 m_haveChildren = true;
99 if (!m_renderer)
100 return;
101
102 AXObjectCache* axCache = m_renderer->document()->axObjectCache();
103
104 // add only rows that are labeled as aria rows
105 HashSet<AccessibilityObject*> appendedRows;
106 unsigned columnCount = 0;
107 for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) {
108
109 if (child->isTableRow() || child->ariaRoleAttribute() == RowRole)
110 addChild(child.get(), appendedRows, columnCount);
111 else {
112 // in case the render tree doesn't match the expected ARIA hierarchy, look at the children
113 if (!child->hasChildren())
114 child->addChildren();
115
116 // Do not navigate children through the Accessibility
117 // children vector to let addChild() check the result
118 // of accessibilityIsIgnored() and make the proper
119 // decision (add the objects or their children).
120 AccessibilityObject* grandChild = 0;
121 for (grandChild = child->firstChild(); grandChild; grandChild = grandChild->nextSibling())
122 addChild(grandChild, appendedRows, columnCount);
123 }
124 }
125
126 // make the columns based on the number of columns in the first body
127 for (unsigned i = 0; i < columnCount; ++i) {
128 AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole));
129 column->setColumnIndex((int)i);
130 column->setParentTable(this);
131 m_columns.append(column);
132 if (!column->accessibilityIsIgnored())
133 m_children.append(column);
134 }
135
136 AccessibilityObject* headerContainerObject = headerContainer();
137 if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
138 m_children.append(headerContainerObject);
139 }
140
cellForColumnAndRow(unsigned column,unsigned row)141 AccessibilityTableCell* AccessibilityARIAGrid::cellForColumnAndRow(unsigned column, unsigned row)
142 {
143 if (!m_renderer)
144 return 0;
145
146 updateChildrenIfNecessary();
147
148 if (column >= columnCount() || row >= rowCount())
149 return 0;
150
151 int intRow = (int)row;
152 int intColumn = (int)column;
153
154 pair<int, int> columnRange;
155 pair<int, int> rowRange;
156
157 // Iterate backwards through the rows in case the desired cell has a rowspan and exists
158 // in a previous row.
159 for (; intRow >= 0; --intRow) {
160 AccessibilityObject* tableRow = m_rows[intRow].get();
161 if (!tableRow)
162 continue;
163
164 AccessibilityChildrenVector children = tableRow->children();
165 unsigned childrenLength = children.size();
166
167 // Since some cells may have colspans, we have to check the actual range of each
168 // cell to determine which is the right one.
169 for (unsigned k = 0; k < childrenLength; ++k) {
170 AccessibilityObject* child = children[k].get();
171 if (!child->isTableCell())
172 continue;
173
174 AccessibilityTableCell* tableCellChild = static_cast<AccessibilityTableCell*>(child);
175 tableCellChild->columnIndexRange(columnRange);
176 tableCellChild->rowIndexRange(rowRange);
177
178 if ((intColumn >= columnRange.first && intColumn < (columnRange.first + columnRange.second))
179 && (intRow >= rowRange.first && intRow < (rowRange.first + rowRange.second)))
180 return tableCellChild;
181 }
182 }
183
184 return 0;
185 }
186
187 } // namespace WebCore
188