[ATK] Use a smart pointer for AccessibilityObject wrapper and remove GTK specific...
[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     AccessibilityObject* coreSelection = core(selection);
100     if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0)
101         return nullptr;
102
103     // This method provides the functionality expected by atk_selection_ref_selection().
104     // According to the ATK documentation for this method, the index is "a gint specifying
105     // the index in the selection set. (e.g. the ith selection as opposed to the ith child)."
106     // There is different API, namely atk_object_ref_accessible_child(), when the ith child
107     // from the set of all children is sought.
108     AccessibilityObject::AccessibilityChildrenVector options;
109     coreSelection->selectedChildren(options);
110     if (index < static_cast<gint>(options.size()))
111         return options.at(index).get();
112
113     return nullptr;
114 }
115
116 static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index)
117 {
118     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
119     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
120
121     AccessibilityObject* coreSelection = core(selection);
122     if (!coreSelection)
123         return FALSE;
124
125     AccessibilityObject* option = optionFromList(selection, index);
126     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
127         option->setSelected(true);
128         return option->isSelected();
129     }
130
131     return FALSE;
132 }
133
134 static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection)
135 {
136     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
137     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
138
139     AccessibilityObject* coreSelection = core(selection);
140     if (!coreSelection)
141         return FALSE;
142
143     AccessibilityObject::AccessibilityChildrenVector selectedItems;
144     if (is<AccessibilityListBox>(*coreSelection)) {
145         // Set the list of selected items to an empty list; then verify that it worked.
146         auto& listBox = downcast<AccessibilityListBox>(*coreSelection);
147         listBox.setSelectedChildren(selectedItems);
148         listBox.selectedChildren(selectedItems);
149         return selectedItems.isEmpty();
150     }
151     return FALSE;
152 }
153
154 static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection, gint index)
155 {
156     g_return_val_if_fail(ATK_SELECTION(selection), nullptr);
157     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr);
158
159     AccessibilityObject* option = optionFromSelection(selection, index);
160     if (option) {
161         auto* child = option->wrapper();
162         g_object_ref(child);
163         return ATK_OBJECT(child);
164     }
165
166     return nullptr;
167 }
168
169 static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection)
170 {
171     g_return_val_if_fail(ATK_SELECTION(selection), 0);
172     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0);
173
174     AccessibilityObject* coreSelection = core(selection);
175     if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
176         return 0;
177
178     if (coreSelection->isMenuList()) {
179         RenderObject* renderer = coreSelection->renderer();
180         if (!renderer)
181             return 0;
182
183         int selectedIndex = downcast<HTMLSelectElement>(renderer->node())->selectedIndex();
184         return selectedIndex >= 0 && selectedIndex < static_cast<int>(downcast<HTMLSelectElement>(renderer->node())->listItems().size());
185     }
186
187     AccessibilityObject::AccessibilityChildrenVector selectedItems;
188     coreSelection->selectedChildren(selectedItems);
189     return static_cast<gint>(selectedItems.size());
190 }
191
192 static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index)
193 {
194     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
195     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
196
197     AccessibilityObject* coreSelection = core(selection);
198     if (!coreSelection)
199         return FALSE;
200
201     AccessibilityObject* option = optionFromList(selection, index);
202     if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
203         return option->isSelected();
204
205     return FALSE;
206 }
207
208 static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection, gint index)
209 {
210     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
211     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
212
213     AccessibilityObject* coreSelection = core(selection);
214     if (!coreSelection)
215         return FALSE;
216
217     AccessibilityObject* option = optionFromSelection(selection, index);
218     if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
219         option->setSelected(false);
220         return !option->isSelected();
221     }
222
223     return FALSE;
224 }
225
226 static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* selection)
227 {
228     g_return_val_if_fail(ATK_SELECTION(selection), FALSE);
229     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE);
230
231     AccessibilityObject* coreSelection = core(selection);
232     if (!coreSelection || !coreSelection->isMultiSelectable())
233         return FALSE;
234
235     if (is<AccessibilityListBox>(*coreSelection)) {
236         const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children();
237         AccessibilityListBox& listBox = downcast<AccessibilityListBox>(*coreSelection);
238         listBox.setSelectedChildren(children);
239         AccessibilityObject::AccessibilityChildrenVector selectedItems;
240         listBox.selectedChildren(selectedItems);
241         return selectedItems.size() == children.size();
242     }
243
244     return FALSE;
245 }
246
247 void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface* iface)
248 {
249     iface->add_selection = webkitAccessibleSelectionAddSelection;
250     iface->clear_selection = webkitAccessibleSelectionClearSelection;
251     iface->ref_selection = webkitAccessibleSelectionRefSelection;
252     iface->get_selection_count = webkitAccessibleSelectionGetSelectionCount;
253     iface->is_child_selected = webkitAccessibleSelectionIsChildSelected;
254     iface->remove_selection = webkitAccessibleSelectionRemoveSelection;
255     iface->select_all_selection = webkitAccessibleSelectionSelectAllSelection;
256 }
257
258 #endif