1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "AccessibilityListBox.h"
31
32#include "AXObjectCache.h"
33#include "AccessibilityListBoxOption.h"
34#include "HTMLNames.h"
35#include "HTMLSelectElement.h"
36#include "HitTestResult.h"
37#include "RenderListBox.h"
38#include "RenderObject.h"
39
40namespace WebCore {
41
42using namespace HTMLNames;
43
44AccessibilityListBox::AccessibilityListBox(RenderObject* renderer)
45    : AccessibilityRenderObject(renderer)
46{
47}
48
49AccessibilityListBox::~AccessibilityListBox()
50{
51}
52
53PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
54{
55    return adoptRef(new AccessibilityListBox(renderer));
56}
57
58bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
59{
60    Node* selectNode = m_renderer->node();
61    if (!selectNode)
62        return false;
63
64    return !toHTMLSelectElement(selectNode)->isDisabledFormControl();
65}
66
67void AccessibilityListBox::addChildren()
68{
69    Node* selectNode = m_renderer->node();
70    if (!selectNode)
71        return;
72
73    m_haveChildren = true;
74
75    for (const auto& listItem : toHTMLSelectElement(selectNode)->listItems()) {
76        // The cast to HTMLElement below is safe because the only other possible listItem type
77        // would be a WMLElement, but WML builds don't use accessibility features at all.
78        AccessibilityObject* listOption = listBoxOptionAccessibilityObject(listItem);
79        if (listOption && !listOption->accessibilityIsIgnored())
80            m_children.append(listOption);
81    }
82}
83
84void AccessibilityListBox::setSelectedChildren(const AccessibilityChildrenVector& children)
85{
86    if (!canSetSelectedChildrenAttribute())
87        return;
88
89    Node* selectNode = m_renderer->node();
90    if (!selectNode)
91        return;
92
93    // disable any selected options
94    for (const auto& child : m_children) {
95        AccessibilityListBoxOption* listBoxOption = toAccessibilityListBoxOption(child.get());
96        if (listBoxOption->isSelected())
97            listBoxOption->setSelected(false);
98    }
99
100    for (const auto& obj : children) {
101        if (obj->roleValue() != ListBoxOptionRole)
102            continue;
103
104        toAccessibilityListBoxOption(obj.get())->setSelected(true);
105    }
106}
107
108void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
109{
110    ASSERT(result.isEmpty());
111
112    if (!hasChildren())
113        addChildren();
114
115    for (const auto& child : m_children) {
116        if (toAccessibilityListBoxOption(child.get())->isSelected())
117            result.append(child.get());
118    }
119}
120
121void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result)
122{
123    ASSERT(result.isEmpty());
124
125    if (!hasChildren())
126        addChildren();
127
128    unsigned length = m_children.size();
129    for (unsigned i = 0; i < length; i++) {
130        if (toRenderListBox(m_renderer)->listIndexIsVisible(i))
131            result.append(m_children[i]);
132    }
133}
134
135AccessibilityObject* AccessibilityListBox::listBoxOptionAccessibilityObject(HTMLElement* element) const
136{
137    // skip hr elements
138    if (!element || element->hasTagName(hrTag))
139        return 0;
140
141    AccessibilityObject* listBoxObject = m_renderer->document().axObjectCache()->getOrCreate(ListBoxOptionRole);
142    toAccessibilityListBoxOption(listBoxObject)->setHTMLElement(element);
143
144    return listBoxObject;
145}
146
147AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const IntPoint& point) const
148{
149    // the internal HTMLSelectElement methods for returning a listbox option at a point
150    // ignore optgroup elements.
151    if (!m_renderer)
152        return 0;
153
154    Node* node = m_renderer->node();
155    if (!node)
156        return 0;
157
158    LayoutRect parentRect = boundingBoxRect();
159
160    AccessibilityObject* listBoxOption = 0;
161    unsigned length = m_children.size();
162    for (unsigned i = 0; i < length; i++) {
163        LayoutRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.location(), i);
164        // The cast to HTMLElement below is safe because the only other possible listItem type
165        // would be a WMLElement, but WML builds don't use accessibility features at all.
166        if (rect.contains(point)) {
167            listBoxOption = m_children[i].get();
168            break;
169        }
170    }
171
172    if (listBoxOption && !listBoxOption->accessibilityIsIgnored())
173        return listBoxOption;
174
175    return axObjectCache()->getOrCreate(m_renderer);
176}
177
178} // namespace WebCore
179