9e87f4f5fbc77eff5285765d66ff747594d022cf
[WebKit-https.git] / Source / WebCore / accessibility / atk / WebKitAccessibleInterfaceSelection.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Jan Alonzo
4  * Copyright (C) 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 "WebKitAccessibleInterfaceSelection.h"
33
34 #if HAVE(ACCESSIBILITY)
35
36 #include "AccessibilityListBox.h"
37 #include "AccessibilityObject.h"
38 #include "HTMLSelectElement.h"
39 #include "RenderObject.h"
40 #include "WebKitAccessibleUtil.h"
41 #include "WebKitAccessibleWrapperAtk.h"
42
43 using namespace WebCore;
44
45 static AccessibilityObject* core(AtkSelection* selection)
46 {
47     if (!WEBKIT_IS_ACCESSIBLE(selection))
48         return nullptr;
49
50     return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(selection));
51 }
52
53 static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
54 {
55     AccessibilityObject* coreSelection = core(selection);
56
57     // Only list boxes and menu lists supported so far.
58     if (!coreSelection->isListBox() && !coreSelection->isMenuList())
59         return nullptr;
60
61     // For list boxes the list object is just itself.
62     if (coreSelection->isListBox())
63         return coreSelection;
64
65     // For menu lists we need to return the first accessible child,
66     // with role MenuListPopupRole, since that's the one holding the list
67     // of items with role MenuListOptionRole.
68     const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
69     if (!children.size())
70         return nullptr;
71
72     AccessibilityObject* listObject = children.at(0).get();
73     if (!listObject->isMenuListPopup())
74         return nullptr;
75
76     return listObject;
77 }
78
79 static AccessibilityObject* optionFromList(AtkSelection* selection, gint index)
80 {
81     AccessibilityObject* coreSelection = core(selection);
82     if (!coreSelection || index < 0)
83         return nullptr;
84
85     // Need to select the proper list object depending on the type.
86     AccessibilityObject* listObject = listObjectForSelection(selection);
87     if (!listObject)
88         return nullptr;
89
90     const AccessibilityObject::AccessibilityChildrenVector& options = listObject->children();
91     if (index < static_cast<gint>(options.size()))
92         return options.at(index).get();
93
94     return nullptr;
95 }
96
97 static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint index)
98 {
99     // i is the ith selection as opposed to the ith child.
100
101     AccessibilityObject* coreSelection = core(selection);
102     if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0)
103         return nullptr;
104
105     int selectedIndex = index;
106     if (coreSelection->isMenuList()) {
107         RenderObject* renderer = coreSelection->renderer();
108         if (!renderer)
109             return nullptr;
110
111         HTMLSelectElement* selectNode = downcast<HTMLSelectElement>(renderer->node());
112         if (!selectNode)
113             return nullptr;
114
115         selectedIndex = selectNode->selectedIndex();
116         const auto& listItems = selectNode->listItems();
117
118         if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
119             return nullptr;
120     }
121
122     return optionFromList(selection, selectedIndex);
123 }
124
125 static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index)
126 {
127     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
128     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
129
130     AccessibilityObject* coreSelection = core(selection);
131     if (!coreSelection)
132         return FALSE;
133
134     AccessibilityObject* option = optionFromList(selection, index);
135     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
136         option->setSelected(true);
137         return option->isSelected();
138     }
139
140     return FALSE;
141 }
142
143 static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection)
144 {
145     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
146     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
147
148     AccessibilityObject* coreSelection = core(selection);
149     if (!coreSelection)
150         return FALSE;
151
152     AccessibilityObject::AccessibilityChildrenVector selectedItems;
153     if (coreSelection->isListBox() || coreSelection->isMenuList()) {
154         // Set the list of selected items to an empty list; then verify that it worked.
155         AccessibilityListBox* listBox = toAccessibilityListBox(coreSelection);
156         listBox->setSelectedChildren(selectedItems);
157         listBox->selectedChildren(selectedItems);
158         return !selectedItems.size();
159     }
160     return FALSE;
161 }
162
163 static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection, gint index)
164 {
165     g_return_val_if_fail(ATK_SELECTION(selection), nullptr);
166     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr);
167
168     AccessibilityObject* option = optionFromSelection(selection, index);
169     if (option) {
170         AtkObject* child = option->wrapper();
171         g_object_ref(child);
172         return child;
173     }
174
175     return nullptr;
176 }
177
178 static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
179 {
180     g_return_val_if_fail(ATK_SELECTION(selection), 0);
181     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0);
182
183     AccessibilityObject* coreSelection = core(selection);
184     if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
185         return 0;
186
187     if (coreSelection->isListBox()) {
188         AccessibilityObject::AccessibilityChildrenVector selectedItems;
189         coreSelection->selectedChildren(selectedItems);
190         return static_cast<gint>(selectedItems.size());
191     }
192
193     if (coreSelection->isMenuList()) {
194         RenderObject* renderer = coreSelection->renderer();
195         if (!renderer)
196             return 0;
197
198         int selectedIndex = downcast<HTMLSelectElement>(renderer->node())->selectedIndex();
199         return selectedIndex >= 0 && selectedIndex < static_cast<int>(downcast<HTMLSelectElement>(renderer->node())->listItems().size());
200     }
201
202     return 0;
203 }
204
205 static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index)
206 {
207     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
208     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
209
210     AccessibilityObject* coreSelection = core(selection);
211     if (!coreSelection)
212         return FALSE;
213
214     AccessibilityObject* option = optionFromList(selection, index);
215     if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
216         return option->isSelected();
217
218     return FALSE;
219 }
220
221 static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection, gint index)
222 {
223     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
224     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
225
226     AccessibilityObject* coreSelection = core(selection);
227     if (!coreSelection)
228         return FALSE;
229
230     // TODO: This is only getting called if i == 0. What is preventing the rest?
231     AccessibilityObject* option = optionFromSelection(selection, index);
232     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
233         option->setSelected(false);
234         return !option->isSelected();
235     }
236
237     return FALSE;
238 }
239
240 static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* selection)
241 {
242     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
243     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
244
245     AccessibilityObject* coreSelection = core(selection);
246     if (!coreSelection || !coreSelection->isMultiSelectable())
247         return FALSE;
248
249     if (coreSelection->isListBox()) {
250         const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
251         AccessibilityListBox* listBox = toAccessibilityListBox(coreSelection);
252         listBox->setSelectedChildren(children);
253         AccessibilityObject::AccessibilityChildrenVector selectedItems;
254         listBox->selectedChildren(selectedItems);
255         return selectedItems.size() == children.size();
256     }
257
258     return FALSE;
259 }
260
261 void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface* iface)
262 {
263     iface->add_selection = webkitAccessibleSelectionAddSelection;
264     iface->clear_selection = webkitAccessibleSelectionClearSelection;
265     iface->ref_selection = webkitAccessibleSelectionRefSelection;
266     iface->get_selection_count = webkitAccessibleSelectionGetSelectionCount;
267     iface->is_child_selected = webkitAccessibleSelectionIsChildSelected;
268     iface->remove_selection = webkitAccessibleSelectionRemoveSelection;
269     iface->select_all_selection = webkitAccessibleSelectionSelectAllSelection;
270 }
271
272 #endif