fa741070089e8030bc8a35f01db246f7cd0c38f1
[WebKit-https.git] / Source / WebCore / accessibility / AccessibilityNodeObject.cpp
1 /*
2 * Copyright (C) 2012, Google 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 "AccessibilityNodeObject.h"
31
32 #include "AXObjectCache.h"
33 #include "AccessibilityImageMapLink.h"
34 #include "AccessibilityListBox.h"
35 #include "AccessibilitySpinButton.h"
36 #include "AccessibilityTable.h"
37 #include "EventNames.h"
38 #include "FloatRect.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "FrameSelection.h"
42 #include "FrameView.h"
43 #include "HTMLAreaElement.h"
44 #include "HTMLFieldSetElement.h"
45 #include "HTMLFormElement.h"
46 #include "HTMLFrameElementBase.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLInputElement.h"
49 #include "HTMLLabelElement.h"
50 #include "HTMLLegendElement.h"
51 #include "HTMLMapElement.h"
52 #include "HTMLNames.h"
53 #include "HTMLOptGroupElement.h"
54 #include "HTMLOptionElement.h"
55 #include "HTMLOptionsCollection.h"
56 #include "HTMLPlugInImageElement.h"
57 #include "HTMLSelectElement.h"
58 #include "HTMLTextAreaElement.h"
59 #include "HTMLTextFormControlElement.h"
60 #include "HitTestRequest.h"
61 #include "HitTestResult.h"
62 #include "LocalizedStrings.h"
63 #include "MathMLNames.h"
64 #include "NodeList.h"
65 #include "Page.h"
66 #include "ProgressTracker.h"
67 #include "Text.h"
68 #include "TextControlInnerElements.h"
69 #include "TextIterator.h"
70 #include "Widget.h"
71 #include "htmlediting.h"
72 #include "visible_units.h"
73 #include <wtf/StdLibExtras.h>
74 #include <wtf/text/StringBuilder.h>
75 #include <wtf/unicode/CharacterNames.h>
76
77 using namespace std;
78
79 namespace WebCore {
80
81 using namespace HTMLNames;
82
83 AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
84     : AccessibilityObject()
85     , m_ariaRole(UnknownRole)
86     , m_childrenDirty(false)
87     , m_roleForMSAA(UnknownRole)
88     , m_node(node)
89 {
90 }
91
92 AccessibilityNodeObject::~AccessibilityNodeObject()
93 {
94     ASSERT(isDetached());
95 }
96
97 void AccessibilityNodeObject::init()
98 {
99     m_role = determineAccessibilityRole();
100 }
101
102 PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
103 {
104     AccessibilityNodeObject* obj = new AccessibilityNodeObject(node);
105     obj->init();
106     return adoptRef(obj);
107 }
108
109 void AccessibilityNodeObject::detach()
110 {
111     clearChildren();
112     AccessibilityObject::detach();
113     m_node = 0;
114 }
115
116 void AccessibilityNodeObject::childrenChanged()
117 {
118     // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
119     if (!node() && !renderer())
120         return;
121
122     axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
123
124     // Go up the accessibility parent chain, but only if the element already exists. This method is
125     // called during render layouts, minimal work should be done. 
126     // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
127     // At the same time, process ARIA live region changes.
128     for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
129         parent->setNeedsToUpdateChildren();
130
131         // These notifications always need to be sent because screenreaders are reliant on them to perform. 
132         // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
133
134         // If this element supports ARIA live regions, then notify the AT of changes.
135         if (parent->supportsARIALiveRegion())
136             axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
137         
138         // If this element is an ARIA text control, notify the AT of changes.
139         if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
140             axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
141     }
142 }
143
144 void AccessibilityNodeObject::updateAccessibilityRole()
145 {
146     bool ignoredStatus = accessibilityIsIgnored();
147     m_role = determineAccessibilityRole();
148     
149     // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
150     if (ignoredStatus != accessibilityIsIgnored())
151         childrenChanged();
152 }
153     
154 AccessibilityObject* AccessibilityNodeObject::firstChild() const
155 {
156     if (!node())
157         return 0;
158     
159     Node* firstChild = node()->firstChild();
160
161     if (!firstChild)
162         return 0;
163     
164     return axObjectCache()->getOrCreate(firstChild);
165 }
166
167 AccessibilityObject* AccessibilityNodeObject::lastChild() const
168 {
169     if (!node())
170         return 0;
171     
172     Node* lastChild = node()->lastChild();
173     if (!lastChild)
174         return 0;
175     
176     return axObjectCache()->getOrCreate(lastChild);
177 }
178
179 AccessibilityObject* AccessibilityNodeObject::previousSibling() const
180 {
181     if (!node())
182         return 0;
183
184     Node* previousSibling = node()->previousSibling();
185     if (!previousSibling)
186         return 0;
187
188     return axObjectCache()->getOrCreate(previousSibling);
189 }
190
191 AccessibilityObject* AccessibilityNodeObject::nextSibling() const
192 {
193     if (!node())
194         return 0;
195
196     Node* nextSibling = node()->nextSibling();
197     if (!nextSibling)
198         return 0;
199
200     return axObjectCache()->getOrCreate(nextSibling);
201 }
202     
203 AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
204 {
205     return parentObject();
206 }
207     
208 AccessibilityObject* AccessibilityNodeObject::parentObject() const
209 {
210     if (!node())
211         return 0;
212
213     Node* parentObj = node()->parentNode();
214     if (parentObj)
215         return axObjectCache()->getOrCreate(parentObj);
216     
217     return 0;
218 }
219
220 LayoutRect AccessibilityNodeObject::elementRect() const
221 {
222     return boundingBoxRect();
223 }
224
225 void AccessibilityNodeObject::setNode(Node* node)
226 {
227     m_node = node;
228 }
229
230 Document* AccessibilityNodeObject::document() const
231 {
232     if (!node())
233         return 0;
234     return node()->document();
235 }
236
237 AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
238 {
239     if (!node())
240         return UnknownRole;
241
242     m_ariaRole = determineAriaRoleAttribute();
243     
244     AccessibilityRole ariaRole = ariaRoleAttribute();
245     if (ariaRole != UnknownRole)
246         return ariaRole;
247
248     if (node()->isLink())
249         return WebCoreLinkRole;
250     if (node()->isTextNode())
251         return StaticTextRole;
252     if (node()->hasTagName(buttonTag))
253         return buttonRoleType();
254     if (node()->hasTagName(inputTag)) {
255         HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
256         if (input->isCheckbox())
257             return CheckBoxRole;
258         if (input->isRadioButton())
259             return RadioButtonRole;
260         if (input->isTextButton())
261             return buttonRoleType();
262         return TextFieldRole;
263     }
264     if (node()->hasTagName(selectTag)) {
265         HTMLSelectElement* selectElement = toHTMLSelectElement(node());
266         return selectElement->multiple() ? ListRole : PopUpButtonRole;
267     }
268     if (node()->isFocusable())
269         return GroupRole;
270     
271     return UnknownRole;
272 }
273
274 void AccessibilityNodeObject::addChildren()
275 {
276     // If the need to add more children in addition to existing children arises, 
277     // childrenChanged should have been called, leaving the object with no children.
278     ASSERT(!m_haveChildren); 
279     
280     if (!m_node)
281         return;
282
283     m_haveChildren = true;
284
285     // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
286     if (renderer() && !m_node->hasTagName(canvasTag))
287         return;
288     
289     for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) {
290         RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child);
291         obj->clearChildren();
292         if (obj->accessibilityIsIgnored()) {
293             AccessibilityChildrenVector children = obj->children();
294             size_t length = children.size();
295             for (size_t i = 0; i < length; ++i)
296                 m_children.append(children[i]);
297         } else {
298             ASSERT(obj->parentObject() == this);
299             m_children.append(obj);
300         }
301     }
302 }
303
304 bool AccessibilityNodeObject::accessibilityIsIgnored() const
305 {
306     return m_role == UnknownRole;
307 }
308
309 bool AccessibilityNodeObject::canSetFocusAttribute() const
310 {
311     Node* node = this->node();
312
313     if (isWebArea())
314         return true;
315     
316     // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
317     // do anything. For example, setFocusedNode() will do nothing if the current focused
318     // node will not relinquish the focus.
319     if (!node)
320         return false;
321
322     if (node->isElementNode() && !static_cast<Element*>(node)->isEnabledFormControl())
323         return false;
324
325     return node->supportsFocus();
326 }
327
328 AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
329 {
330     const AtomicString& ariaRole = getAttribute(roleAttr);
331     if (ariaRole.isNull() || ariaRole.isEmpty())
332         return UnknownRole;
333     
334     AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
335
336     // ARIA states if an item can get focus, it should not be presentational.
337     if (role == PresentationalRole && canSetFocusAttribute())
338         return UnknownRole;
339
340     if (role == ButtonRole)
341         role = buttonRoleType();
342
343     if (role == TextAreaRole && !ariaIsMultiline())
344         role = TextFieldRole;
345
346     role = remapAriaRoleDueToParent(role);
347     
348     if (role)
349         return role;
350
351     return UnknownRole;
352 }
353
354 AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
355 {
356     return m_ariaRole;
357 }
358
359 AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
360 {
361     // Some objects change their role based on their parent.
362     // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop. 
363     // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
364     // https://bugs.webkit.org/show_bug.cgi?id=65174
365
366     if (role != ListBoxOptionRole && role != MenuItemRole)
367         return role;
368     
369     for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
370         AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
371
372         // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
373         if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
374             return MenuItemRole;
375         // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
376         if (role == MenuItemRole && parentAriaRole == GroupRole)
377             return MenuButtonRole;
378         
379         // If the parent had a different role, then we don't need to continue searching up the chain.
380         if (parentAriaRole)
381             break;
382     }
383     
384     return role;
385 }   
386
387 } // namespace WebCore