1/*
2 * Copyright (C) 2008 Nuanti Ltd.
3 * Copyright (C) 2009 Jan Alonzo
4 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
5 *
6 * Portions from Mozilla a11y, copyright as follows:
7 *
8 * The Original Code is mozilla.org code.
9 *
10 * The Initial Developer of the Original Code is
11 * Sun Microsystems, Inc.
12 * Portions created by the Initial Developer are Copyright (C) 2002
13 * the Initial Developer. All Rights Reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public License
26 * along with this library; see the file COPYING.LIB. If not, write to
27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
29 */
30
31#include "config.h"
32#include "WebKitAccessibleInterfaceTable.h"
33
34#if HAVE(ACCESSIBILITY)
35
36#include "AccessibilityListBox.h"
37#include "AccessibilityObject.h"
38#include "AccessibilityTable.h"
39#include "AccessibilityTableCell.h"
40#include "HTMLTableCaptionElement.h"
41#include "HTMLTableElement.h"
42#include "RenderElement.h"
43#include "WebKitAccessible.h"
44#include "WebKitAccessibleInterfaceText.h"
45#include "WebKitAccessibleUtil.h"
46
47using namespace WebCore;
48
49static AccessibilityObject* core(AtkTable* table)
50{
51 if (!WEBKIT_IS_ACCESSIBLE(table))
52 return nullptr;
53
54 return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table));
55}
56
57static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
58{
59 AccessibilityObject* accTable = core(table);
60 if (is<AccessibilityTable>(*accTable))
61 return downcast<AccessibilityTable>(*accTable).cellForColumnAndRow(column, row);
62 return nullptr;
63}
64
65static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
66{
67 // Calculate the cell's index as if we had a traditional Gtk+ table in
68 // which cells are all direct children of the table, arranged row-first.
69 AccessibilityObject::AccessibilityChildrenVector allCells;
70 axTable->cells(allCells);
71 AccessibilityObject::AccessibilityChildrenVector::iterator position;
72 position = std::find(allCells.begin(), allCells.end(), axCell);
73 if (position == allCells.end())
74 return -1;
75 return position - allCells.begin();
76}
77
78static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
79{
80 AccessibilityObject* accTable = core(table);
81 if (is<AccessibilityTable>(*accTable)) {
82 AccessibilityObject::AccessibilityChildrenVector allCells;
83 downcast<AccessibilityTable>(*accTable).cells(allCells);
84 if (0 <= index && static_cast<unsigned>(index) < allCells.size())
85 return downcast<AccessibilityTableCell>(allCells[index].get());
86 }
87 return nullptr;
88}
89
90static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint column)
91{
92 g_return_val_if_fail(ATK_TABLE(table), 0);
93 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
94
95 AccessibilityTableCell* axCell = cell(table, row, column);
96 if (!axCell)
97 return 0;
98
99 auto* cell = axCell->wrapper();
100 if (!cell)
101 return 0;
102
103 // This method transfers full ownership over the returned
104 // AtkObject, so an extra reference is needed here.
105 return ATK_OBJECT(g_object_ref(cell));
106}
107
108static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint column)
109{
110 g_return_val_if_fail(ATK_TABLE(table), -1);
111 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
112
113 AccessibilityTableCell* axCell = cell(table, row, column);
114 AccessibilityTable* axTable = downcast<AccessibilityTable>(core(table));
115 return cellIndex(axCell, axTable);
116}
117
118static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index)
119{
120 g_return_val_if_fail(ATK_TABLE(table), -1);
121 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
122
123 AccessibilityTableCell* axCell = cellAtIndex(table, index);
124 if (axCell) {
125 std::pair<unsigned, unsigned> columnRange;
126 axCell->columnIndexRange(columnRange);
127 return columnRange.first;
128 }
129 return -1;
130}
131
132static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index)
133{
134 g_return_val_if_fail(ATK_TABLE(table), -1);
135 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
136
137 AccessibilityTableCell* axCell = cellAtIndex(table, index);
138 if (axCell) {
139 std::pair<unsigned, unsigned> rowRange;
140 axCell->rowIndexRange(rowRange);
141 return rowRange.first;
142 }
143 return -1;
144}
145
146static gint webkitAccessibleTableGetNColumns(AtkTable* table)
147{
148 g_return_val_if_fail(ATK_TABLE(table), 0);
149 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
150
151 AccessibilityObject* accTable = core(table);
152 if (!is<AccessibilityTable>(*accTable))
153 return 0;
154
155 if (int columnCount = downcast<AccessibilityTable>(*accTable).axColumnCount())
156 return columnCount;
157
158 return downcast<AccessibilityTable>(*accTable).columnCount();
159}
160
161static gint webkitAccessibleTableGetNRows(AtkTable* table)
162{
163 g_return_val_if_fail(ATK_TABLE(table), 0);
164 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
165
166 AccessibilityObject* accTable = core(table);
167 if (!is<AccessibilityTable>(*accTable))
168 return 0;
169
170 if (int rowCount = downcast<AccessibilityTable>(*accTable).axRowCount())
171 return rowCount;
172
173 return downcast<AccessibilityTable>(*accTable).rowCount();
174}
175
176static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gint column)
177{
178 g_return_val_if_fail(ATK_TABLE(table), 0);
179 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
180
181 AccessibilityTableCell* axCell = cell(table, row, column);
182 if (axCell) {
183 std::pair<unsigned, unsigned> columnRange;
184 axCell->columnIndexRange(columnRange);
185 return columnRange.second;
186 }
187 return 0;
188}
189
190static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint column)
191{
192 g_return_val_if_fail(ATK_TABLE(table), 0);
193 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
194
195 AccessibilityTableCell* axCell = cell(table, row, column);
196 if (axCell) {
197 std::pair<unsigned, unsigned> rowRange;
198 axCell->rowIndexRange(rowRange);
199 return rowRange.second;
200 }
201 return 0;
202}
203
204static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint column)
205{
206 g_return_val_if_fail(ATK_TABLE(table), 0);
207 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
208
209 AccessibilityObject* accTable = core(table);
210 if (is<AccessibilityTable>(*accTable)) {
211 AccessibilityObject::AccessibilityChildrenVector columnHeaders;
212 downcast<AccessibilityTable>(*accTable).columnHeaders(columnHeaders);
213
214 for (const auto& columnHeader : columnHeaders) {
215 std::pair<unsigned, unsigned> columnRange;
216 downcast<AccessibilityTableCell>(*columnHeader).columnIndexRange(columnRange);
217 if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second)
218 return ATK_OBJECT(columnHeader->wrapper());
219 }
220 }
221 return nullptr;
222}
223
224static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
225{
226 g_return_val_if_fail(ATK_TABLE(table), 0);
227 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
228
229 AccessibilityObject* accTable = core(table);
230 if (is<AccessibilityTable>(*accTable)) {
231 AccessibilityObject::AccessibilityChildrenVector rowHeaders;
232 downcast<AccessibilityTable>(*accTable).rowHeaders(rowHeaders);
233
234 for (const auto& rowHeader : rowHeaders) {
235 std::pair<unsigned, unsigned> rowRange;
236 downcast<AccessibilityTableCell>(*rowHeader).rowIndexRange(rowRange);
237 if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second)
238 return ATK_OBJECT(rowHeader->wrapper());
239 }
240 }
241 return nullptr;
242}
243
244static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table)
245{
246 g_return_val_if_fail(ATK_TABLE(table), nullptr);
247 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), nullptr);
248
249 AccessibilityObject* accTable = core(table);
250 if (accTable->isAccessibilityRenderObject()) {
251 Node* node = accTable->node();
252 if (is<HTMLTableElement>(node)) {
253 auto caption = downcast<HTMLTableElement>(*node).caption();
254 if (caption)
255 return ATK_OBJECT(AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->element())->wrapper());
256 }
257 }
258 return nullptr;
259}
260
261static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column)
262{
263 g_return_val_if_fail(ATK_TABLE(table), 0);
264 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
265
266 AtkObject* columnHeader = atk_table_get_column_header(table, column);
267 if (columnHeader && ATK_IS_TEXT(columnHeader))
268 return atk_text_get_text(ATK_TEXT(columnHeader), 0, -1);
269
270 return 0;
271}
272
273static const gchar* webkitAccessibleTableGetRowDescription(AtkTable* table, gint row)
274{
275 g_return_val_if_fail(ATK_TABLE(table), 0);
276 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
277
278 AtkObject* rowHeader = atk_table_get_row_header(table, row);
279 if (rowHeader && ATK_IS_TEXT(rowHeader))
280 return atk_text_get_text(ATK_TEXT(rowHeader), 0, -1);
281
282 return 0;
283}
284
285void webkitAccessibleTableInterfaceInit(AtkTableIface* iface)
286{
287 iface->ref_at = webkitAccessibleTableRefAt;
288 iface->get_index_at = webkitAccessibleTableGetIndexAt;
289 iface->get_column_at_index = webkitAccessibleTableGetColumnAtIndex;
290 iface->get_row_at_index = webkitAccessibleTableGetRowAtIndex;
291 iface->get_n_columns = webkitAccessibleTableGetNColumns;
292 iface->get_n_rows = webkitAccessibleTableGetNRows;
293 iface->get_column_extent_at = webkitAccessibleTableGetColumnExtentAt;
294 iface->get_row_extent_at = webkitAccessibleTableGetRowExtentAt;
295 iface->get_column_header = webkitAccessibleTableGetColumnHeader;
296 iface->get_row_header = webkitAccessibleTableGetRowHeader;
297 iface->get_caption = webkitAccessibleTableGetCaption;
298 iface->get_column_description = webkitAccessibleTableGetColumnDescription;
299 iface->get_row_description = webkitAccessibleTableGetRowDescription;
300}
301
302#endif
303