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 "WebKitAccessible.h" |
41 | #include "WebKitAccessibleUtil.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 |
259 | |