2 * Copyright (C) 2012, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "AccessibilityNodeObject.h"
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"
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"
66 #include "ProgressTracker.h"
68 #include "TextControlInnerElements.h"
69 #include "TextIterator.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>
81 using namespace HTMLNames;
83 AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
84 : AccessibilityObject()
85 , m_ariaRole(UnknownRole)
86 , m_childrenDirty(false)
87 , m_roleForMSAA(UnknownRole)
92 AccessibilityNodeObject::~AccessibilityNodeObject()
97 void AccessibilityNodeObject::init()
99 m_role = determineAccessibilityRole();
102 PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
104 AccessibilityNodeObject* obj = new AccessibilityNodeObject(node);
106 return adoptRef(obj);
109 void AccessibilityNodeObject::detach()
112 AccessibilityObject::detach();
116 void AccessibilityNodeObject::childrenChanged()
118 // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
119 if (!node() && !renderer())
122 axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
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();
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.
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);
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);
144 void AccessibilityNodeObject::updateAccessibilityRole()
146 bool ignoredStatus = accessibilityIsIgnored();
147 m_role = determineAccessibilityRole();
149 // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
150 if (ignoredStatus != accessibilityIsIgnored())
154 AccessibilityObject* AccessibilityNodeObject::firstChild() const
159 Node* firstChild = node()->firstChild();
164 return axObjectCache()->getOrCreate(firstChild);
167 AccessibilityObject* AccessibilityNodeObject::lastChild() const
172 Node* lastChild = node()->lastChild();
176 return axObjectCache()->getOrCreate(lastChild);
179 AccessibilityObject* AccessibilityNodeObject::previousSibling() const
184 Node* previousSibling = node()->previousSibling();
185 if (!previousSibling)
188 return axObjectCache()->getOrCreate(previousSibling);
191 AccessibilityObject* AccessibilityNodeObject::nextSibling() const
196 Node* nextSibling = node()->nextSibling();
200 return axObjectCache()->getOrCreate(nextSibling);
203 AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
205 return parentObject();
208 AccessibilityObject* AccessibilityNodeObject::parentObject() const
213 Node* parentObj = node()->parentNode();
215 return axObjectCache()->getOrCreate(parentObj);
220 LayoutRect AccessibilityNodeObject::elementRect() const
222 return boundingBoxRect();
225 void AccessibilityNodeObject::setNode(Node* node)
230 Document* AccessibilityNodeObject::document() const
234 return node()->document();
237 AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
242 m_ariaRole = determineAriaRoleAttribute();
244 AccessibilityRole ariaRole = ariaRoleAttribute();
245 if (ariaRole != UnknownRole)
248 if (node()->isLink())
249 return WebCoreLinkRole;
250 if (node()->isTextNode())
251 return StaticTextRole;
252 if (node()->hasTagName(buttonTag))
253 return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
254 if (node()->hasTagName(inputTag)) {
255 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
256 if (input->isCheckbox())
258 if (input->isRadioButton())
259 return RadioButtonRole;
260 if (input->isTextButton())
261 return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
262 return TextFieldRole;
264 if (node()->hasTagName(selectTag)) {
265 HTMLSelectElement* selectElement = toHTMLSelectElement(node());
266 return selectElement->multiple() ? ListRole : PopUpButtonRole;
268 if (node()->isFocusable())
274 void AccessibilityNodeObject::addChildren()
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);
283 m_haveChildren = true;
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))
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]);
298 ASSERT(obj->parentObject() == this);
299 m_children.append(obj);
304 bool AccessibilityNodeObject::accessibilityIsIgnored() const
306 return m_role == UnknownRole;
309 bool AccessibilityNodeObject::canSetFocusAttribute() const
311 Node* node = this->node();
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.
322 if (node->isElementNode() && !static_cast<Element*>(node)->isEnabledFormControl())
325 return node->supportsFocus();
328 AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
330 const AtomicString& ariaRole = getAttribute(roleAttr);
331 if (ariaRole.isNull() || ariaRole.isEmpty())
334 AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
336 // ARIA states if an item can get focus, it should not be presentational.
337 if (role == PresentationalRole && canSetFocusAttribute())
340 if (role == ButtonRole && ariaHasPopup())
341 role = PopUpButtonRole;
343 if (role == TextAreaRole && !ariaIsMultiline())
344 role = TextFieldRole;
346 role = remapAriaRoleDueToParent(role);
354 AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
359 AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
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
366 if (role != ListBoxOptionRole && role != MenuItemRole)
369 for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
370 AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
372 // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
373 if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
375 // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
376 if (role == MenuItemRole && parentAriaRole == GroupRole)
377 return MenuButtonRole;
379 // If the parent had a different role, then we don't need to continue searching up the chain.
387 } // namespace WebCore