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 "HTMLSelectElement.h"
41#include "HTMLTableCaptionElement.h"
42#include "HTMLTableElement.h"
43#include "RenderElement.h"
44#include "WebKitAccessibleInterfaceText.h"
45#include "WebKitAccessibleUtil.h"
46#include "WebKitAccessibleWrapperAtk.h"
47
48using namespace WebCore;
49
50static AccessibilityObject* core(AtkTable* table)
51{
52    if (!WEBKIT_IS_ACCESSIBLE(table))
53        return 0;
54
55    return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table));
56}
57
58static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
59{
60    AccessibilityObject* accTable = core(table);
61    if (accTable->isAccessibilityRenderObject())
62        return toAccessibilityTable(accTable)->cellForColumnAndRow(column, row);
63    return 0;
64}
65
66static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
67{
68    // Calculate the cell's index as if we had a traditional Gtk+ table in
69    // which cells are all direct children of the table, arranged row-first.
70    AccessibilityObject::AccessibilityChildrenVector allCells;
71    axTable->cells(allCells);
72    AccessibilityObject::AccessibilityChildrenVector::iterator position;
73    position = std::find(allCells.begin(), allCells.end(), axCell);
74    if (position == allCells.end())
75        return -1;
76    return position - allCells.begin();
77}
78
79static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
80{
81    AccessibilityObject* accTable = core(table);
82    if (accTable->isAccessibilityRenderObject()) {
83        AccessibilityObject::AccessibilityChildrenVector allCells;
84        toAccessibilityTable(accTable)->cells(allCells);
85        if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
86            AccessibilityObject* accCell = allCells.at(index).get();
87            return toAccessibilityTableCell(accCell);
88        }
89    }
90    return 0;
91}
92
93static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint column)
94{
95    g_return_val_if_fail(ATK_TABLE(table), 0);
96    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
97
98    AccessibilityTableCell* axCell = cell(table, row, column);
99    if (!axCell)
100        return 0;
101
102    AtkObject* cell = axCell->wrapper();
103    if (!cell)
104        return 0;
105
106    // This method transfers full ownership over the returned
107    // AtkObject, so an extra reference is needed here.
108    return ATK_OBJECT(g_object_ref(cell));
109}
110
111static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint column)
112{
113    g_return_val_if_fail(ATK_TABLE(table), -1);
114    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
115
116    AccessibilityTableCell* axCell = cell(table, row, column);
117    AccessibilityTable* axTable = toAccessibilityTable(core(table));
118    return cellIndex(axCell, axTable);
119}
120
121static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index)
122{
123    g_return_val_if_fail(ATK_TABLE(table), -1);
124    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
125
126    AccessibilityTableCell* axCell = cellAtIndex(table, index);
127    if (axCell) {
128        std::pair<unsigned, unsigned> columnRange;
129        axCell->columnIndexRange(columnRange);
130        return columnRange.first;
131    }
132    return -1;
133}
134
135static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index)
136{
137    g_return_val_if_fail(ATK_TABLE(table), -1);
138    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1);
139
140    AccessibilityTableCell* axCell = cellAtIndex(table, index);
141    if (axCell) {
142        std::pair<unsigned, unsigned> rowRange;
143        axCell->rowIndexRange(rowRange);
144        return rowRange.first;
145    }
146    return -1;
147}
148
149static gint webkitAccessibleTableGetNColumns(AtkTable* table)
150{
151    g_return_val_if_fail(ATK_TABLE(table), 0);
152    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
153
154    AccessibilityObject* accTable = core(table);
155    if (accTable->isAccessibilityRenderObject())
156        return toAccessibilityTable(accTable)->columnCount();
157    return 0;
158}
159
160static gint webkitAccessibleTableGetNRows(AtkTable* table)
161{
162    g_return_val_if_fail(ATK_TABLE(table), 0);
163    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
164
165    AccessibilityObject* accTable = core(table);
166    if (accTable->isAccessibilityRenderObject())
167        return toAccessibilityTable(accTable)->rowCount();
168    return 0;
169}
170
171static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gint column)
172{
173    g_return_val_if_fail(ATK_TABLE(table), 0);
174    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
175
176    AccessibilityTableCell* axCell = cell(table, row, column);
177    if (axCell) {
178        std::pair<unsigned, unsigned> columnRange;
179        axCell->columnIndexRange(columnRange);
180        return columnRange.second;
181    }
182    return 0;
183}
184
185static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint column)
186{
187    g_return_val_if_fail(ATK_TABLE(table), 0);
188    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
189
190    AccessibilityTableCell* axCell = cell(table, row, column);
191    if (axCell) {
192        std::pair<unsigned, unsigned> rowRange;
193        axCell->rowIndexRange(rowRange);
194        return rowRange.second;
195    }
196    return 0;
197}
198
199static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint column)
200{
201    g_return_val_if_fail(ATK_TABLE(table), 0);
202    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
203
204    AccessibilityObject* accTable = core(table);
205    if (accTable->isAccessibilityRenderObject()) {
206        AccessibilityObject::AccessibilityChildrenVector columnHeaders;
207        toAccessibilityTable(accTable)->columnHeaders(columnHeaders);
208
209        for (const auto& columnHeader : columnHeaders) {
210            std::pair<unsigned, unsigned> columnRange;
211            toAccessibilityTableCell(columnHeader.get())->columnIndexRange(columnRange);
212            if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second)
213                return columnHeader->wrapper();
214        }
215    }
216    return 0;
217}
218
219static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
220{
221    g_return_val_if_fail(ATK_TABLE(table), 0);
222    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
223
224    AccessibilityObject* accTable = core(table);
225    if (accTable->isAccessibilityRenderObject()) {
226        AccessibilityObject::AccessibilityChildrenVector rowHeaders;
227        toAccessibilityTable(accTable)->rowHeaders(rowHeaders);
228
229        for (const auto& rowHeader : rowHeaders) {
230            std::pair<unsigned, unsigned> rowRange;
231            toAccessibilityTableCell(rowHeader.get())->rowIndexRange(rowRange);
232            if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second)
233                return rowHeader->wrapper();
234        }
235    }
236    return 0;
237}
238
239static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table)
240{
241    g_return_val_if_fail(ATK_TABLE(table), 0);
242    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
243
244    AccessibilityObject* accTable = core(table);
245    if (accTable->isAccessibilityRenderObject()) {
246        Node* node = accTable->node();
247        if (node && isHTMLTableElement(node)) {
248            HTMLTableCaptionElement* caption = toHTMLTableElement(node)->caption();
249            if (caption)
250                return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->element())->wrapper();
251        }
252    }
253    return 0;
254}
255
256static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column)
257{
258    g_return_val_if_fail(ATK_TABLE(table), 0);
259    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
260
261    AtkObject* columnHeader = atk_table_get_column_header(table, column);
262    if (columnHeader && ATK_IS_TEXT(columnHeader))
263        return atk_text_get_text(ATK_TEXT(columnHeader), 0, -1);
264
265    return 0;
266}
267
268static const gchar* webkitAccessibleTableGetRowDescription(AtkTable* table, gint row)
269{
270    g_return_val_if_fail(ATK_TABLE(table), 0);
271    returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0);
272
273    AtkObject* rowHeader = atk_table_get_row_header(table, row);
274    if (rowHeader && ATK_IS_TEXT(rowHeader))
275        return atk_text_get_text(ATK_TEXT(rowHeader), 0, -1);
276
277    return 0;
278}
279
280void webkitAccessibleTableInterfaceInit(AtkTableIface* iface)
281{
282    iface->ref_at = webkitAccessibleTableRefAt;
283    iface->get_index_at = webkitAccessibleTableGetIndexAt;
284    iface->get_column_at_index = webkitAccessibleTableGetColumnAtIndex;
285    iface->get_row_at_index = webkitAccessibleTableGetRowAtIndex;
286    iface->get_n_columns = webkitAccessibleTableGetNColumns;
287    iface->get_n_rows = webkitAccessibleTableGetNRows;
288    iface->get_column_extent_at = webkitAccessibleTableGetColumnExtentAt;
289    iface->get_row_extent_at = webkitAccessibleTableGetRowExtentAt;
290    iface->get_column_header = webkitAccessibleTableGetColumnHeader;
291    iface->get_row_header = webkitAccessibleTableGetRowHeader;
292    iface->get_caption = webkitAccessibleTableGetCaption;
293    iface->get_column_description = webkitAccessibleTableGetColumnDescription;
294    iface->get_row_description = webkitAccessibleTableGetRowDescription;
295}
296
297#endif
298