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 Computer, 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
40using namespace std;
41
42namespace WebCore {
43
44using namespace HTMLNames;
45
46AccessibilityListBox::AccessibilityListBox(RenderObject* renderer)
47    : AccessibilityRenderObject(renderer)
48{
49}
50
51AccessibilityListBox::~AccessibilityListBox()
52{
53}
54
55PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
56{
57    return adoptRef(new AccessibilityListBox(renderer));
58}
59
60bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
61{
62    Node* selectNode = m_renderer->node();
63    if (!selectNode)
64        return false;
65
66    return !static_cast<HTMLSelectElement*>(selectNode)->disabled();
67}
68
69void AccessibilityListBox::addChildren()
70{
71    Node* selectNode = m_renderer->node();
72    if (!selectNode)
73        return;
74
75    m_haveChildren = true;
76
77    const Vector<Element*>& listItems = static_cast<HTMLSelectElement*>(selectNode)->listItems();
78    unsigned length = listItems.size();
79    for (unsigned i = 0; i < length; i++) {
80        // The cast to HTMLElement below is safe because the only other possible listItem type
81        // would be a WMLElement, but WML builds don't use accessibility features at all.
82        AccessibilityObject* listOption = listBoxOptionAccessibilityObject(toHTMLElement(listItems[i]));
83        if (listOption && !listOption->accessibilityIsIgnored())
84            m_children.append(listOption);
85    }
86}
87
88void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& children)
89{
90    if (!canSetSelectedChildrenAttribute())
91        return;
92
93    Node* selectNode = m_renderer->node();
94    if (!selectNode)
95        return;
96
97    // disable any selected options
98    unsigned length = m_children.size();
99    for (unsigned i = 0; i < length; i++) {
100        AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(m_children[i].get());
101        if (listBoxOption->isSelected())
102            listBoxOption->setSelected(false);
103    }
104
105    length = children.size();
106    for (unsigned i = 0; i < length; i++) {
107        AccessibilityObject* obj = children[i].get();
108        if (obj->roleValue() != ListBoxOptionRole)
109            continue;
110
111        static_cast<AccessibilityListBoxOption*>(obj)->setSelected(true);
112    }
113}
114
115void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
116{
117    ASSERT(result.isEmpty());
118
119    if (!hasChildren())
120        addChildren();
121
122    unsigned length = m_children.size();
123    for (unsigned i = 0; i < length; i++) {
124        if (static_cast<AccessibilityListBoxOption*>(m_children[i].get())->isSelected())
125            result.append(m_children[i]);
126    }
127}
128
129void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result)
130{
131    ASSERT(result.isEmpty());
132
133    if (!hasChildren())
134        addChildren();
135
136    unsigned length = m_children.size();
137    for (unsigned i = 0; i < length; i++) {
138        if (toRenderListBox(m_renderer)->listIndexIsVisible(i))
139            result.append(m_children[i]);
140    }
141}
142
143AccessibilityObject* AccessibilityListBox::listBoxOptionAccessibilityObject(HTMLElement* element) const
144{
145    // skip hr elements
146    if (!element || element->hasTagName(hrTag))
147        return 0;
148
149    AccessibilityObject* listBoxObject = m_renderer->document()->axObjectCache()->getOrCreate(ListBoxOptionRole);
150    static_cast<AccessibilityListBoxOption*>(listBoxObject)->setHTMLElement(element);
151
152    return listBoxObject;
153}
154
155bool AccessibilityListBox::accessibilityIsIgnored() const
156{
157    AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
158    if (decision == IncludeObject)
159        return false;
160    if (decision == IgnoreObject)
161        return true;
162
163    return false;
164}
165
166AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const IntPoint& point) const
167{
168    // the internal HTMLSelectElement methods for returning a listbox option at a point
169    // ignore optgroup elements.
170    if (!m_renderer)
171        return 0;
172
173    Node* node = m_renderer->node();
174    if (!node)
175        return 0;
176
177    IntRect parentRect = boundingBoxRect();
178
179    AccessibilityObject* listBoxOption = 0;
180    unsigned length = m_children.size();
181    for (unsigned i = 0; i < length; i++) {
182        IntRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), i);
183        // The cast to HTMLElement below is safe because the only other possible listItem type
184        // would be a WMLElement, but WML builds don't use accessibility features at all.
185        if (rect.contains(point)) {
186            listBoxOption = m_children[i].get();
187            break;
188        }
189    }
190
191    if (listBoxOption && !listBoxOption->accessibilityIsIgnored())
192        return listBoxOption;
193
194    return axObjectCache()->getOrCreate(m_renderer);
195}
196
197} // namespace WebCore
198