| /* |
| * Copyright (C) 2008 Nuanti Ltd. |
| * Copyright (C) 2009 Jan Alonzo |
| * Copyright (C) 2010, 2011, 2012 Igalia S.L. |
| * |
| * Portions from Mozilla a11y, copyright as follows: |
| * |
| * The Original Code is mozilla.org code. |
| * |
| * The Initial Developer of the Original Code is |
| * Sun Microsystems, Inc. |
| * Portions created by the Initial Developer are Copyright (C) 2002 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "WebKitAccessibleInterfaceSelection.h" |
| |
| #if ENABLE(ACCESSIBILITY) && USE(ATK) |
| |
| #include "AccessibilityListBox.h" |
| #include "AccessibilityObject.h" |
| #include "HTMLSelectElement.h" |
| #include "RenderObject.h" |
| #include "WebKitAccessible.h" |
| #include "WebKitAccessibleUtil.h" |
| |
| using namespace WebCore; |
| |
| static AccessibilityObject* core(AtkSelection* selection) |
| { |
| if (!WEBKIT_IS_ACCESSIBLE(selection)) |
| return nullptr; |
| |
| return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(selection)); |
| } |
| |
| static AXCoreObject* listObjectForCoreSelection(AccessibilityObject* coreSelection) |
| { |
| // Only list boxes and menu lists supported so far. |
| if (!coreSelection->isListBox() && !coreSelection->isMenuList()) |
| return nullptr; |
| |
| // For list boxes the list object is just itself. |
| if (coreSelection->isListBox()) |
| return coreSelection; |
| |
| // For menu lists we need to return the first accessible child, |
| // with role MenuListPopupRole, since that's the one holding the list |
| // of items with role MenuListOptionRole. |
| const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children(); |
| if (!children.size()) |
| return nullptr; |
| |
| AXCoreObject* listObject = children.at(0).get(); |
| if (!listObject->isMenuListPopup()) |
| return nullptr; |
| |
| return listObject; |
| } |
| |
| static AXCoreObject* optionFromList(AtkSelection* selection, gint index) |
| { |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection || index < 0) |
| return nullptr; |
| |
| // Need to select the proper list object depending on the type. |
| AXCoreObject* listObject = listObjectForCoreSelection(coreSelection); |
| if (!listObject) |
| return nullptr; |
| |
| const AccessibilityObject::AccessibilityChildrenVector& options = listObject->children(); |
| if (index < static_cast<gint>(options.size())) |
| return options.at(index).get(); |
| |
| return nullptr; |
| } |
| |
| static AXCoreObject* optionFromSelection(AtkSelection* selection, gint index) |
| { |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0) |
| return nullptr; |
| |
| // This method provides the functionality expected by atk_selection_ref_selection(). |
| // According to the ATK documentation for this method, the index is "a gint specifying |
| // the index in the selection set. (e.g. the ith selection as opposed to the ith child)." |
| // There is different API, namely atk_object_ref_accessible_child(), when the ith child |
| // from the set of all children is sought. |
| AccessibilityObject::AccessibilityChildrenVector options; |
| coreSelection->selectedChildren(options); |
| if (index < static_cast<gint>(options.size())) |
| return options.at(index).get(); |
| |
| return nullptr; |
| } |
| |
| static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), FALSE); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection) |
| return FALSE; |
| |
| AXCoreObject* option = optionFromList(selection, index); |
| if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { |
| option->setSelected(true); |
| return option->isSelected(); |
| } |
| |
| return FALSE; |
| } |
| |
| static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), FALSE); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection) |
| return FALSE; |
| |
| AccessibilityObject::AccessibilityChildrenVector selectedItems; |
| if (is<AccessibilityListBox>(*coreSelection)) { |
| // Set the list of selected items to an empty list; then verify that it worked. |
| auto& listBox = downcast<AccessibilityListBox>(*coreSelection); |
| listBox.setSelectedChildren(selectedItems); |
| listBox.selectedChildren(selectedItems); |
| return selectedItems.isEmpty(); |
| } |
| return FALSE; |
| } |
| |
| static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection, gint index) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), nullptr); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr); |
| |
| AXCoreObject* option = optionFromSelection(selection, index); |
| if (option) { |
| auto* child = option->wrapper(); |
| g_object_ref(child); |
| return ATK_OBJECT(child); |
| } |
| |
| return nullptr; |
| } |
| |
| static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), 0); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection || !coreSelection->isAccessibilityRenderObject()) |
| return 0; |
| |
| if (coreSelection->isMenuList()) { |
| RenderObject* renderer = coreSelection->renderer(); |
| if (!renderer) |
| return 0; |
| |
| int selectedIndex = downcast<HTMLSelectElement>(renderer->node())->selectedIndex(); |
| return selectedIndex >= 0 && selectedIndex < static_cast<int>(downcast<HTMLSelectElement>(renderer->node())->listItems().size()); |
| } |
| |
| AccessibilityObject::AccessibilityChildrenVector selectedItems; |
| coreSelection->selectedChildren(selectedItems); |
| return static_cast<gint>(selectedItems.size()); |
| } |
| |
| static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), FALSE); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection) |
| return FALSE; |
| |
| AXCoreObject* option = optionFromList(selection, index); |
| if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) |
| return option->isSelected(); |
| |
| return FALSE; |
| } |
| |
| static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection, gint index) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), FALSE); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection) |
| return FALSE; |
| |
| AXCoreObject* option = optionFromSelection(selection, index); |
| if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { |
| option->setSelected(false); |
| return !option->isSelected(); |
| } |
| |
| return FALSE; |
| } |
| |
| static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* selection) |
| { |
| g_return_val_if_fail(ATK_SELECTION(selection), FALSE); |
| returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); |
| |
| AccessibilityObject* coreSelection = core(selection); |
| if (!coreSelection || !coreSelection->isMultiSelectable()) |
| return FALSE; |
| |
| if (is<AccessibilityListBox>(*coreSelection)) { |
| const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children(); |
| AccessibilityListBox& listBox = downcast<AccessibilityListBox>(*coreSelection); |
| listBox.setSelectedChildren(children); |
| AccessibilityObject::AccessibilityChildrenVector selectedItems; |
| listBox.selectedChildren(selectedItems); |
| return selectedItems.size() == children.size(); |
| } |
| |
| return FALSE; |
| } |
| |
| void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface* iface) |
| { |
| iface->add_selection = webkitAccessibleSelectionAddSelection; |
| iface->clear_selection = webkitAccessibleSelectionClearSelection; |
| iface->ref_selection = webkitAccessibleSelectionRefSelection; |
| iface->get_selection_count = webkitAccessibleSelectionGetSelectionCount; |
| iface->is_child_selected = webkitAccessibleSelectionIsChildSelected; |
| iface->remove_selection = webkitAccessibleSelectionRemoveSelection; |
| iface->select_all_selection = webkitAccessibleSelectionSelectAllSelection; |
| } |
| |
| #endif |