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 43using namespace WebCore; 44 45static AccessibilityObject* core(AtkSelection* selection) 46{ 47 if (!WEBKIT_IS_ACCESSIBLE(selection)) 48 return nullptr; 49 50 return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(selection)); 51} 52 53static 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 79static 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 97static 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 = toHTMLSelectElement(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 125static 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 143static 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 163static 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 178static 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 = toHTMLSelectElement(renderer->node())->selectedIndex(); 199 return selectedIndex >= 0 && selectedIndex < static_cast<int>(toHTMLSelectElement(renderer->node())->listItems().size()); 200 } 201 202 return 0; 203} 204 205static 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 221static 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 240static 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 261void 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 273