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