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 "AccessibilityListBoxOption.h"
31
32#include "AXObjectCache.h"
33#include "AccessibilityListBox.h"
34#include "Element.h"
35#include "HTMLElement.h"
36#include "HTMLNames.h"
37#include "HTMLOptGroupElement.h"
38#include "HTMLOptionElement.h"
39#include "HTMLSelectElement.h"
40#include "IntRect.h"
41#include "RenderListBox.h"
42#include "RenderObject.h"
43
44namespace WebCore {
45
46using namespace HTMLNames;
47
48AccessibilityListBoxOption::AccessibilityListBoxOption()
49    : m_optionElement(0)
50{
51}
52
53AccessibilityListBoxOption::~AccessibilityListBoxOption()
54{
55}
56
57PassRefPtr<AccessibilityListBoxOption> AccessibilityListBoxOption::create()
58{
59    return adoptRef(new AccessibilityListBoxOption());
60}
61
62bool AccessibilityListBoxOption::isEnabled() const
63{
64    if (!m_optionElement)
65        return false;
66
67    if (isHTMLOptGroupElement(m_optionElement))
68        return false;
69
70    if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
71        return false;
72
73    if (m_optionElement->hasAttribute(disabledAttr))
74        return false;
75
76    return true;
77}
78
79bool AccessibilityListBoxOption::isSelected() const
80{
81    if (!m_optionElement)
82        return false;
83
84    if (!isHTMLOptionElement(m_optionElement))
85        return false;
86
87    return toHTMLOptionElement(m_optionElement)->selected();
88}
89
90bool AccessibilityListBoxOption::isSelectedOptionActive() const
91{
92    HTMLSelectElement* listBoxParentNode = listBoxOptionParentNode();
93    if (!listBoxParentNode)
94        return false;
95
96    return listBoxParentNode->activeSelectionEndListIndex() == listBoxOptionIndex();
97}
98
99LayoutRect AccessibilityListBoxOption::elementRect() const
100{
101    LayoutRect rect;
102    if (!m_optionElement)
103        return rect;
104
105    HTMLSelectElement* listBoxParentNode = listBoxOptionParentNode();
106    if (!listBoxParentNode)
107        return rect;
108
109    RenderObject* listBoxRenderer = listBoxParentNode->renderer();
110    if (!listBoxRenderer)
111        return rect;
112
113    LayoutRect parentRect = listBoxRenderer->document().axObjectCache()->getOrCreate(listBoxRenderer)->boundingBoxRect();
114    int index = listBoxOptionIndex();
115    if (index != -1)
116        rect = toRenderListBox(listBoxRenderer)->itemBoundingBoxRect(parentRect.location(), index);
117
118    return rect;
119}
120
121bool AccessibilityListBoxOption::computeAccessibilityIsIgnored() const
122{
123    if (!m_optionElement)
124        return true;
125
126    if (accessibilityIsIgnoredByDefault())
127        return true;
128
129    return parentObject()->accessibilityIsIgnored();
130}
131
132bool AccessibilityListBoxOption::canSetSelectedAttribute() const
133{
134    if (!m_optionElement)
135        return false;
136
137    if (!isHTMLOptionElement(m_optionElement))
138        return false;
139
140    if (m_optionElement->isDisabledFormControl())
141        return false;
142
143    HTMLSelectElement* selectElement = listBoxOptionParentNode();
144    if (selectElement && selectElement->isDisabledFormControl())
145        return false;
146
147    return true;
148}
149
150String AccessibilityListBoxOption::stringValue() const
151{
152    if (!m_optionElement)
153        return String();
154
155    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
156    if (!ariaLabel.isNull())
157        return ariaLabel;
158
159    if (isHTMLOptionElement(m_optionElement))
160        return toHTMLOptionElement(m_optionElement)->text();
161
162    if (isHTMLOptGroupElement(m_optionElement))
163        return toHTMLOptGroupElement(m_optionElement)->groupLabelText();
164
165    return String();
166}
167
168Element* AccessibilityListBoxOption::actionElement() const
169{
170    return m_optionElement;
171}
172
173AccessibilityObject* AccessibilityListBoxOption::parentObject() const
174{
175    HTMLSelectElement* parentNode = listBoxOptionParentNode();
176    if (!parentNode)
177        return 0;
178
179    return m_optionElement->document().axObjectCache()->getOrCreate(parentNode);
180}
181
182void AccessibilityListBoxOption::setSelected(bool selected)
183{
184    HTMLSelectElement* selectElement = listBoxOptionParentNode();
185    if (!selectElement)
186        return;
187
188    if (!canSetSelectedAttribute())
189        return;
190
191    bool isOptionSelected = isSelected();
192    if ((isOptionSelected && selected) || (!isOptionSelected && !selected))
193        return;
194
195    // Convert from the entire list index to the option index.
196    int optionIndex = selectElement->listToOptionIndex(listBoxOptionIndex());
197    selectElement->accessKeySetSelectedIndex(optionIndex);
198}
199
200HTMLSelectElement* AccessibilityListBoxOption::listBoxOptionParentNode() const
201{
202    if (!m_optionElement)
203        return 0;
204
205    if (isHTMLOptionElement(m_optionElement))
206        return toHTMLOptionElement(m_optionElement)->ownerSelectElement();
207
208    if (isHTMLOptGroupElement(m_optionElement))
209        return toHTMLOptGroupElement(m_optionElement)->ownerSelectElement();
210
211    return 0;
212}
213
214int AccessibilityListBoxOption::listBoxOptionIndex() const
215{
216    if (!m_optionElement)
217        return -1;
218
219    HTMLSelectElement* selectElement = listBoxOptionParentNode();
220    if (!selectElement)
221        return -1;
222
223    const auto& listItems = selectElement->listItems();
224    unsigned length = listItems.size();
225    for (unsigned i = 0; i < length; i++)
226        if (listItems[i] == m_optionElement)
227            return i;
228
229    return -1;
230}
231
232} // namespace WebCore
233