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 "LabelableElement.h"
63 #include "LocalizedStrings.h"
64 #include "MathMLNames.h"
66 #include "NodeTraversal.h"
68 #include "ProgressTracker.h"
70 #include "TextControlInnerElements.h"
71 #include "TextIterator.h"
72 #include "UserGestureIndicator.h"
74 #include "htmlediting.h"
75 #include "visible_units.h"
76 #include <wtf/StdLibExtras.h>
77 #include <wtf/text/StringBuilder.h>
78 #include <wtf/unicode/CharacterNames.h>
84 using namespace HTMLNames;
86 AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
87 : AccessibilityObject()
88 , m_ariaRole(UnknownRole)
89 , m_childrenDirty(false)
90 , m_roleForMSAA(UnknownRole)
95 AccessibilityNodeObject::~AccessibilityNodeObject()
100 void AccessibilityNodeObject::init()
102 m_role = determineAccessibilityRole();
105 PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
107 AccessibilityNodeObject* obj = new AccessibilityNodeObject(node);
109 return adoptRef(obj);
112 void AccessibilityNodeObject::detach()
115 AccessibilityObject::detach();
119 void AccessibilityNodeObject::childrenChanged()
121 // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
122 if (!node() && !renderer())
125 axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
127 // Go up the accessibility parent chain, but only if the element already exists. This method is
128 // called during render layouts, minimal work should be done.
129 // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
130 // At the same time, process ARIA live region changes.
131 for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
132 parent->setNeedsToUpdateChildren();
134 // These notifications always need to be sent because screenreaders are reliant on them to perform.
135 // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
137 // If this element supports ARIA live regions, then notify the AT of changes.
138 if (parent->supportsARIALiveRegion())
139 axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
141 // If this element is an ARIA text control, notify the AT of changes.
142 if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
143 axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
147 void AccessibilityNodeObject::updateAccessibilityRole()
149 bool ignoredStatus = accessibilityIsIgnored();
150 m_role = determineAccessibilityRole();
152 // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
153 if (ignoredStatus != accessibilityIsIgnored())
157 AccessibilityObject* AccessibilityNodeObject::firstChild() const
162 Node* firstChild = node()->firstChild();
167 return axObjectCache()->getOrCreate(firstChild);
170 AccessibilityObject* AccessibilityNodeObject::lastChild() const
175 Node* lastChild = node()->lastChild();
179 return axObjectCache()->getOrCreate(lastChild);
182 AccessibilityObject* AccessibilityNodeObject::previousSibling() const
187 Node* previousSibling = node()->previousSibling();
188 if (!previousSibling)
191 return axObjectCache()->getOrCreate(previousSibling);
194 AccessibilityObject* AccessibilityNodeObject::nextSibling() const
199 Node* nextSibling = node()->nextSibling();
203 return axObjectCache()->getOrCreate(nextSibling);
206 AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
208 return parentObject();
211 AccessibilityObject* AccessibilityNodeObject::parentObject() const
216 Node* parentObj = node()->parentNode();
218 return axObjectCache()->getOrCreate(parentObj);
223 LayoutRect AccessibilityNodeObject::elementRect() const
225 return boundingBoxRect();
228 LayoutRect AccessibilityNodeObject::boundingBoxRect() const
230 // AccessibilityNodeObjects have no mechanism yet to return a size or position.
231 // For now, let's return the position of the ancestor that does have a position,
232 // and make it the width of that parent, and about the height of a line of text, so that it's clear the object is a child of the parent.
234 LayoutRect boundingBox;
236 for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
237 if (positionProvider->isAccessibilityRenderObject()) {
238 LayoutRect parentRect = positionProvider->elementRect();
239 boundingBox.setSize(LayoutSize(parentRect.width(), LayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
240 boundingBox.setLocation(parentRect.location());
248 void AccessibilityNodeObject::setNode(Node* node)
253 Document* AccessibilityNodeObject::document() const
257 return node()->document();
260 AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
265 m_ariaRole = determineAriaRoleAttribute();
267 AccessibilityRole ariaRole = ariaRoleAttribute();
268 if (ariaRole != UnknownRole)
271 if (node()->isLink())
272 return WebCoreLinkRole;
273 if (node()->isTextNode())
274 return StaticTextRole;
275 if (node()->hasTagName(buttonTag))
276 return buttonRoleType();
277 if (node()->hasTagName(inputTag)) {
278 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
279 if (input->isCheckbox())
281 if (input->isRadioButton())
282 return RadioButtonRole;
283 if (input->isTextButton())
284 return buttonRoleType();
285 if (input->isRangeControl())
288 #if ENABLE(INPUT_TYPE_COLOR)
289 const AtomicString& type = input->getAttribute(typeAttr);
290 if (equalIgnoringCase(type, "color"))
291 return ColorWellRole;
294 return TextFieldRole;
296 if (node()->hasTagName(selectTag)) {
297 HTMLSelectElement* selectElement = toHTMLSelectElement(node());
298 return selectElement->multiple() ? ListBoxRole : PopUpButtonRole;
300 if (node()->hasTagName(textareaTag))
304 if (node()->hasTagName(divTag))
306 if (node()->hasTagName(pTag))
307 return ParagraphRole;
308 if (node()->hasTagName(labelTag))
310 if (node()->isFocusable())
316 void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index)
321 // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
322 // or its visibility has changed. In the latter case, this child may have a stale child cached.
323 // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
324 child->clearChildren();
326 if (child->accessibilityIsIgnored()) {
327 AccessibilityChildrenVector children = child->children();
328 size_t length = children.size();
329 for (size_t i = 0; i < length; ++i)
330 m_children.insert(index + i, children[i]);
332 ASSERT(child->parentObject() == this);
333 m_children.insert(index, child);
337 void AccessibilityNodeObject::addChild(AccessibilityObject* child)
339 insertChild(child, m_children.size());
342 void AccessibilityNodeObject::addChildren()
344 // If the need to add more children in addition to existing children arises,
345 // childrenChanged should have been called, leaving the object with no children.
346 ASSERT(!m_haveChildren);
351 m_haveChildren = true;
353 // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
354 if (renderer() && !m_node->hasTagName(canvasTag))
357 for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
358 addChild(axObjectCache()->getOrCreate(child));
361 bool AccessibilityNodeObject::canHaveChildren() const
363 // If this is an AccessibilityRenderObject, then it's okay if this object
364 // doesn't have a node - there are some renderers that don't have associated
365 // nodes, like scroll areas and css-generated text.
366 if (!node() && !isAccessibilityRenderObject())
369 // Elements that should not have children
370 switch (roleValue()) {
373 case PopUpButtonRole:
375 case RadioButtonRole:
377 case ToggleButtonRole:
379 case ListBoxOptionRole:
387 bool AccessibilityNodeObject::accessibilityIsIgnored() const
389 // If this element is within a parent that cannot have children, it should not be exposed.
390 if (isDescendantOfBarrenParent())
393 return m_role == UnknownRole;
396 bool AccessibilityNodeObject::canvasHasFallbackContent() const
398 Node* node = this->node();
399 if (!node || !node->hasTagName(canvasTag))
402 // If it has any children that are elements, we'll assume it might be fallback
403 // content. If it has no children or its only children are not elements
404 // (e.g. just text nodes), it doesn't have fallback content.
405 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
406 if (child->isElementNode())
413 bool AccessibilityNodeObject::isImageButton() const
415 return isNativeImage() && isButton();
418 bool AccessibilityNodeObject::isAnchor() const
420 return !isNativeImage() && isLink();
423 bool AccessibilityNodeObject::isNativeTextControl() const
425 Node* node = this->node();
429 if (node->hasTagName(textareaTag))
432 if (node->hasTagName(inputTag)) {
433 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
434 return input->isText() || input->isNumberField();
440 bool AccessibilityNodeObject::isSearchField() const
442 Node* node = this->node();
446 HTMLInputElement* inputElement = node->toInputElement();
450 if (inputElement->isSearchField())
453 // Some websites don't label their search fields as such. However, they will
454 // use the word "search" in either the form or input type. This won't catch every case,
455 // but it will catch google.com for example.
457 // Check the node name of the input type, sometimes it's "search".
458 const AtomicString& nameAttribute = getAttribute(nameAttr);
459 if (nameAttribute.contains("search", false))
462 // Check the form action and the name, which will sometimes be "search".
463 HTMLFormElement* form = inputElement->form();
464 if (form && (form->name().contains("search", false) || form->action().contains("search", false)))
470 bool AccessibilityNodeObject::isNativeImage() const
472 Node* node = this->node();
476 if (node->hasTagName(imgTag))
479 if (node->hasTagName(appletTag) || node->hasTagName(embedTag) || node->hasTagName(objectTag))
482 if (node->hasTagName(inputTag)) {
483 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
484 return input->isImageButton();
490 bool AccessibilityNodeObject::isImage() const
492 return roleValue() == ImageRole;
495 bool AccessibilityNodeObject::isPasswordField() const
497 Node* node = this->node();
498 if (!node || !node->isHTMLElement())
501 if (ariaRoleAttribute() != UnknownRole)
504 HTMLInputElement* inputElement = node->toInputElement();
508 return inputElement->isPasswordField();
511 bool AccessibilityNodeObject::isInputImage() const
513 Node* node = this->node();
517 if (roleValue() == ButtonRole && node->hasTagName(inputTag)) {
518 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
519 return input->isImageButton();
525 bool AccessibilityNodeObject::isProgressIndicator() const
527 return roleValue() == ProgressIndicatorRole;
530 bool AccessibilityNodeObject::isSlider() const
532 return roleValue() == SliderRole;
535 bool AccessibilityNodeObject::isMenuRelated() const
537 switch (roleValue()) {
548 bool AccessibilityNodeObject::isMenu() const
550 return roleValue() == MenuRole;
553 bool AccessibilityNodeObject::isMenuBar() const
555 return roleValue() == MenuBarRole;
558 bool AccessibilityNodeObject::isMenuButton() const
560 return roleValue() == MenuButtonRole;
563 bool AccessibilityNodeObject::isMenuItem() const
565 return roleValue() == MenuItemRole;
568 bool AccessibilityNodeObject::isNativeCheckboxOrRadio() const
570 Node* node = this->node();
574 HTMLInputElement* input = node->toInputElement();
576 return input->isCheckbox() || input->isRadioButton();
581 bool AccessibilityNodeObject::isEnabled() const
583 if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
586 Node* node = this->node();
587 if (!node || !node->isElementNode())
590 return toElement(node)->isEnabledFormControl();
593 bool AccessibilityNodeObject::isIndeterminate() const
595 Node* node = this->node();
599 HTMLInputElement* inputElement = node->toInputElement();
603 return inputElement->isIndeterminate();
606 bool AccessibilityNodeObject::isPressed() const
611 Node* node = this->node();
615 // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
616 if (ariaRoleAttribute() == ButtonRole) {
617 if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
622 return node->active();
625 bool AccessibilityNodeObject::isChecked() const
627 Node* node = this->node();
631 // First test for native checkedness semantics
632 HTMLInputElement* inputElement = node->toInputElement();
634 return inputElement->shouldAppearChecked();
636 // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
637 AccessibilityRole ariaRole = ariaRoleAttribute();
638 if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
639 if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
644 // Otherwise it's not checked
648 bool AccessibilityNodeObject::isHovered() const
650 Node* node = this->node();
654 return node->hovered();
657 bool AccessibilityNodeObject::isMultiSelectable() const
659 const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
660 if (equalIgnoringCase(ariaMultiSelectable, "true"))
662 if (equalIgnoringCase(ariaMultiSelectable, "false"))
665 return node() && node()->hasTagName(selectTag) && toHTMLSelectElement(node())->multiple();
668 bool AccessibilityNodeObject::isReadOnly() const
670 Node* node = this->node();
674 if (node->hasTagName(textareaTag))
675 return static_cast<HTMLTextAreaElement*>(node)->readOnly();
677 if (node->hasTagName(inputTag)) {
678 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
679 if (input->isTextField())
680 return input->readOnly();
683 return !node->rendererIsEditable();
686 bool AccessibilityNodeObject::isRequired() const
688 if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
691 Node* n = this->node();
692 if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
693 return static_cast<HTMLFormControlElement*>(n)->isRequired();
698 int AccessibilityNodeObject::headingLevel() const
700 // headings can be in block flow and non-block flow
701 Node* node = this->node();
705 if (ariaRoleAttribute() == HeadingRole)
706 return getAttribute(aria_levelAttr).toInt();
708 if (node->hasTagName(h1Tag))
711 if (node->hasTagName(h2Tag))
714 if (node->hasTagName(h3Tag))
717 if (node->hasTagName(h4Tag))
720 if (node->hasTagName(h5Tag))
723 if (node->hasTagName(h6Tag))
729 String AccessibilityNodeObject::valueDescription() const
734 return getAttribute(aria_valuetextAttr).string();
737 bool AccessibilityNodeObject::isARIARange() const
739 switch (m_ariaRole) {
740 case ProgressIndicatorRole:
750 float AccessibilityNodeObject::valueForRange() const
752 if (node() && node()->hasTagName(inputTag)) {
753 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
754 if (input->isRangeControl())
755 return input->valueAsNumber();
761 return getAttribute(aria_valuenowAttr).toFloat();
764 float AccessibilityNodeObject::maxValueForRange() const
766 if (node() && node()->hasTagName(inputTag)) {
767 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
768 if (input->isRangeControl())
769 return input->maximum();
775 return getAttribute(aria_valuemaxAttr).toFloat();
778 float AccessibilityNodeObject::minValueForRange() const
780 if (node() && node()->hasTagName(inputTag)) {
781 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
782 if (input->isRangeControl())
783 return input->minimum();
789 return getAttribute(aria_valueminAttr).toFloat();
792 float AccessibilityNodeObject::stepValueForRange() const
794 return getAttribute(stepAttr).toFloat();
797 bool AccessibilityNodeObject::isHeading() const
799 return roleValue() == HeadingRole;
802 bool AccessibilityNodeObject::isLink() const
804 return roleValue() == WebCoreLinkRole;
807 bool AccessibilityNodeObject::isControl() const
809 Node* node = this->node();
813 return ((node->isElementNode() && toElement(node)->isFormControlElement())
814 || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
817 bool AccessibilityNodeObject::isFieldset() const
819 Node* node = this->node();
823 return node->hasTagName(fieldsetTag);
826 bool AccessibilityNodeObject::isGroup() const
828 return roleValue() == GroupRole;
831 AccessibilityObject* AccessibilityNodeObject::selectedRadioButton()
836 AccessibilityObject::AccessibilityChildrenVector children = this->children();
838 // Find the child radio button that is selected (ie. the intValue == 1).
839 size_t size = children.size();
840 for (size_t i = 0; i < size; ++i) {
841 AccessibilityObject* object = children[i].get();
842 if (object->roleValue() == RadioButtonRole && object->checkboxOrRadioValue() == ButtonStateOn)
848 AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
853 // Find the child tab item that is selected (ie. the intValue == 1).
854 AccessibilityObject::AccessibilityChildrenVector tabs;
857 AccessibilityObject::AccessibilityChildrenVector children = this->children();
858 size_t size = tabs.size();
859 for (size_t i = 0; i < size; ++i) {
860 AccessibilityObject* object = children[i].get();
861 if (object->isTabItem() && object->isChecked())
867 AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
869 if (isNativeCheckboxOrRadio())
870 return isChecked() ? ButtonStateOn : ButtonStateOff;
872 return AccessibilityObject::checkboxOrRadioValue();
875 Element* AccessibilityNodeObject::anchorElement() const
877 Node* node = this->node();
881 AXObjectCache* cache = axObjectCache();
883 // search up the DOM tree for an anchor element
884 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
885 for ( ; node; node = node->parentNode()) {
886 if (node->hasTagName(aTag) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
887 return toElement(node);
893 Element* AccessibilityNodeObject::actionElement() const
895 Node* node = this->node();
899 if (node->hasTagName(inputTag)) {
900 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
901 if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
903 } else if (node->hasTagName(buttonTag))
904 return toElement(node);
906 if (isFileUploadButton())
907 return toElement(node);
909 if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
910 return toElement(node);
913 return toElement(node);
915 if (node->hasTagName(selectTag))
916 return toElement(node);
918 switch (roleValue()) {
920 case PopUpButtonRole:
921 case ToggleButtonRole:
925 return toElement(node);
930 Element* elt = anchorElement();
932 elt = mouseButtonListener();
936 Element* AccessibilityNodeObject::mouseButtonListener() const
938 Node* node = this->node();
942 // check if our parent is a mouse button listener
943 while (node && !node->isElementNode())
944 node = node->parentNode();
949 // FIXME: Do the continuation search like anchorElement does
950 for (Element* element = toElement(node); element; element = element->parentElement()) {
951 if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
958 bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
960 for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
961 if (!object->canHaveChildren())
968 void AccessibilityNodeObject::alterSliderValue(bool increase)
970 if (roleValue() != SliderRole)
973 if (!getAttribute(stepAttr).isEmpty())
974 changeValueByStep(increase);
976 changeValueByPercent(increase ? 5 : -5);
979 void AccessibilityNodeObject::increment()
981 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
982 alterSliderValue(true);
985 void AccessibilityNodeObject::decrement()
987 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
988 alterSliderValue(false);
991 void AccessibilityNodeObject::changeValueByStep(bool increase)
993 float step = stepValueForRange();
994 float value = valueForRange();
996 value += increase ? step : -step;
998 setValue(String::number(value));
1000 axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
1003 void AccessibilityNodeObject::changeValueByPercent(float percentChange)
1005 float range = maxValueForRange() - minValueForRange();
1006 float value = valueForRange();
1008 value += range * (percentChange / 100);
1009 setValue(String::number(value));
1011 axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
1014 bool AccessibilityNodeObject::isGenericFocusableElement() const
1016 if (!canSetFocusAttribute())
1019 // If it's a control, it's not generic.
1023 // If it has an aria role, it's not generic.
1024 if (m_ariaRole != UnknownRole)
1027 // If the content editable attribute is set on this element, that's the reason
1028 // it's focusable, and existing logic should handle this case already - so it's not a
1029 // generic focusable element.
1031 if (hasContentEditableAttributeSet())
1034 // The web area and body element are both focusable, but existing logic handles these
1035 // cases already, so we don't need to include them here.
1036 if (roleValue() == WebAreaRole)
1038 if (node() && node()->hasTagName(bodyTag))
1041 // An SVG root is focusable by default, but it's probably not interactive, so don't
1042 // include it. It can still be made accessible by giving it an ARIA role.
1043 if (roleValue() == SVGRootRole)
1049 HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const
1051 if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
1054 const AtomicString& id = element->getIdAttribute();
1055 if (!id.isEmpty()) {
1056 if (HTMLLabelElement* label = element->treeScope()->labelElementForId(id))
1060 for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) {
1061 if (parent->hasTagName(labelTag))
1062 return static_cast<HTMLLabelElement*>(parent);
1068 String AccessibilityNodeObject::ariaAccessibilityDescription() const
1070 String ariaLabeledBy = ariaLabeledByAttribute();
1071 if (!ariaLabeledBy.isEmpty())
1072 return ariaLabeledBy;
1074 const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1075 if (!ariaLabel.isEmpty())
1081 static Element* siblingWithAriaRole(String role, Node* node)
1083 for (Node* sibling = node->parentNode()->firstChild(); sibling; sibling = sibling->nextSibling()) {
1084 if (sibling->isElementNode()) {
1085 const AtomicString& siblingAriaRole = toElement(sibling)->getAttribute(roleAttr);
1086 if (equalIgnoringCase(siblingAriaRole, role))
1087 return toElement(sibling);
1094 Element* AccessibilityNodeObject::menuElementForMenuButton() const
1096 if (ariaRoleAttribute() != MenuButtonRole)
1099 return siblingWithAriaRole("menu", node());
1102 AccessibilityObject* AccessibilityNodeObject::menuForMenuButton() const
1104 return axObjectCache()->getOrCreate(menuElementForMenuButton());
1107 Element* AccessibilityNodeObject::menuItemElementForMenu() const
1109 if (ariaRoleAttribute() != MenuRole)
1112 return siblingWithAriaRole("menuitem", node());
1115 AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const
1117 Element* menuItem = menuItemElementForMenu();
1120 // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
1121 AccessibilityObject* menuItemAX = axObjectCache()->getOrCreate(menuItem);
1122 if (menuItemAX->isMenuButton())
1128 void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder)
1130 Node* node = this->node();
1134 bool isInputTag = node->hasTagName(inputTag);
1135 if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1136 HTMLLabelElement* label = labelForElement(toElement(node));
1138 AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
1139 textOrder.append(AccessibilityText(label->innerText(), LabelByElementText, labelObject));
1144 AccessibilityObject* titleUIElement = this->titleUIElement();
1146 textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement));
1149 void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
1152 String webAreaText = alternativeTextForWebArea();
1153 if (!webAreaText.isEmpty())
1154 textOrder.append(AccessibilityText(webAreaText, AlternativeText));
1158 ariaLabeledByText(textOrder);
1160 const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1161 if (!ariaLabel.isEmpty())
1162 textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
1164 if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
1165 // Images should use alt as long as the attribute is present, even if empty.
1166 // Otherwise, it should fallback to other methods, like the title attribute.
1167 const AtomicString& alt = getAttribute(altAttr);
1169 textOrder.append(AccessibilityText(alt, AlternativeText));
1173 Node* node = this->node();
1174 if (node && node->isElementNode() && toElement(node)->isMathMLElement())
1175 textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText));
1179 void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder) const
1181 Node* node = this->node();
1185 bool isInputTag = node->hasTagName(inputTag);
1187 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1188 if (input->isTextButton()) {
1189 textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText));
1194 // If this node isn't rendered, there's no inner text we can extract from a select element.
1195 if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1198 bool useTextUnderElement = false;
1200 switch (roleValue()) {
1201 case PopUpButtonRole:
1202 // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1203 if (node->hasTagName(selectTag))
1206 case ToggleButtonRole:
1208 case ListBoxOptionRole:
1209 case MenuButtonRole:
1211 case RadioButtonRole:
1213 useTextUnderElement = true;
1219 // If it's focusable but it's not content editable or a known control type, then it will appear to
1220 // the user as a single atomic object, so we should use its text as the default title.
1221 if (isHeading() || isLink() || isGenericFocusableElement())
1222 useTextUnderElement = true;
1224 if (useTextUnderElement) {
1225 String text = textUnderElement();
1226 if (!text.isEmpty())
1227 textOrder.append(AccessibilityText(text, ChildrenText));
1231 void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) const
1233 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1234 if (!ariaHelp.isEmpty())
1235 textOrder.append(AccessibilityText(ariaHelp, HelpText));
1237 String describedBy = ariaDescribedByAttribute();
1238 if (!describedBy.isEmpty())
1239 textOrder.append(AccessibilityText(describedBy, SummaryText));
1241 // Add help type text that is derived from ancestors.
1242 for (Node* curr = node(); curr; curr = curr->parentNode()) {
1243 const AtomicString& summary = getAttribute(summaryAttr);
1244 if (!summary.isEmpty())
1245 textOrder.append(AccessibilityText(summary, SummaryText));
1247 // The title attribute should be used as help text unless it is already being used as descriptive text.
1248 const AtomicString& title = getAttribute(titleAttr);
1249 if (!title.isEmpty())
1250 textOrder.append(AccessibilityText(title, TitleTagText));
1252 // Only take help text from an ancestor element if its a group or an unknown role. If help was
1253 // added to those kinds of elements, it is likely it was meant for a child element.
1254 AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
1258 AccessibilityRole role = axObj->roleValue();
1259 if (role != GroupRole && role != UnknownRole)
1264 void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textOrder)
1266 titleElementText(textOrder);
1267 alternativeText(textOrder);
1268 visibleText(textOrder);
1269 helpText(textOrder);
1271 String placeholder = placeholderValue();
1272 if (!placeholder.isEmpty())
1273 textOrder.append(AccessibilityText(placeholder, PlaceholderText));
1276 void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
1278 String ariaLabeledBy = ariaLabeledByAttribute();
1279 if (!ariaLabeledBy.isEmpty()) {
1280 Vector<Element*> elements;
1281 ariaLabeledByElements(elements);
1283 Vector<RefPtr<AccessibilityObject> > axElements;
1284 unsigned length = elements.size();
1285 for (unsigned k = 0; k < length; k++) {
1286 RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(elements[k]);
1287 axElements.append(axElement);
1290 textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElements));
1294 String AccessibilityNodeObject::alternativeTextForWebArea() const
1296 // The WebArea description should follow this order:
1297 // aria-label on the <html>
1298 // title on the <html>
1299 // <title> inside the <head> (of it was set through JS)
1300 // name on the <html>
1302 // aria-label on the <iframe>
1303 // title on the <iframe>
1304 // name on the <iframe>
1306 Document* document = this->document();
1310 // Check if the HTML element has an aria-label for the webpage.
1311 if (Element* documentElement = document->documentElement()) {
1312 const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
1313 if (!ariaLabel.isEmpty())
1317 Node* owner = document->ownerElement();
1319 if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1320 const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
1321 if (!title.isEmpty())
1323 return static_cast<HTMLFrameElementBase*>(owner)->getNameAttribute();
1325 if (owner->isHTMLElement())
1326 return toHTMLElement(owner)->getNameAttribute();
1329 String documentTitle = document->title();
1330 if (!documentTitle.isEmpty())
1331 return documentTitle;
1333 owner = document->body();
1334 if (owner && owner->isHTMLElement())
1335 return toHTMLElement(owner)->getNameAttribute();
1340 String AccessibilityNodeObject::accessibilityDescription() const
1342 // Static text should not have a description, it should only have a stringValue.
1343 if (roleValue() == StaticTextRole)
1346 String ariaDescription = ariaAccessibilityDescription();
1347 if (!ariaDescription.isEmpty())
1348 return ariaDescription;
1350 if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
1351 // Images should use alt as long as the attribute is present, even if empty.
1352 // Otherwise, it should fallback to other methods, like the title attribute.
1353 const AtomicString& alt = getAttribute(altAttr);
1359 Node* node = this->node();
1360 if (node && node->isElementNode() && toElement(node)->isMathMLElement())
1361 return getAttribute(MathMLNames::alttextAttr);
1364 // An element's descriptive text is comprised of title() (what's visible on the screen) and accessibilityDescription() (other descriptive text).
1365 // Both are used to generate what a screen reader speaks.
1366 // If this point is reached (i.e. there's no accessibilityDescription) and there's no title(), we should fallback to using the title attribute.
1367 // The title attribute is normally used as help text (because it is a tooltip), but if there is nothing else available, this should be used (according to ARIA).
1368 if (title().isEmpty())
1369 return getAttribute(titleAttr);
1374 String AccessibilityNodeObject::helpText() const
1376 Node* node = this->node();
1380 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1381 if (!ariaHelp.isEmpty())
1384 String describedBy = ariaDescribedByAttribute();
1385 if (!describedBy.isEmpty())
1388 String description = accessibilityDescription();
1389 for (Node* curr = node; curr; curr = curr->parentNode()) {
1390 if (curr->isHTMLElement()) {
1391 const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
1392 if (!summary.isEmpty())
1395 // The title attribute should be used as help text unless it is already being used as descriptive text.
1396 const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
1397 if (!title.isEmpty() && description != title)
1401 // Only take help text from an ancestor element if its a group or an unknown role. If help was
1402 // added to those kinds of elements, it is likely it was meant for a child element.
1403 AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
1405 AccessibilityRole role = axObj->roleValue();
1406 if (role != GroupRole && role != UnknownRole)
1414 unsigned AccessibilityNodeObject::hierarchicalLevel() const
1416 Node* node = this->node();
1417 if (!node || !node->isElementNode())
1419 Element* element = toElement(node);
1420 String ariaLevel = element->getAttribute(aria_levelAttr);
1421 if (!ariaLevel.isEmpty())
1422 return ariaLevel.toInt();
1424 // Only tree item will calculate its level through the DOM currently.
1425 if (roleValue() != TreeItemRole)
1428 // Hierarchy leveling starts at 1, to match the aria-level spec.
1429 // We measure tree hierarchy by the number of groups that the item is within.
1431 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1432 AccessibilityRole parentRole = parent->roleValue();
1433 if (parentRole == GroupRole)
1435 else if (parentRole == TreeRole)
1442 // When building the textUnderElement for an object, determine whether or not
1443 // we should include the inner text of this given descendant object or skip it.
1444 static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj)
1446 // Consider this hypothetical example:
1449 // Table of contents
1451 // <a href="#start">Jump to start of book</a>
1453 // <li><a href="#1">Chapter 1</a></li>
1454 // <li><a href="#1">Chapter 2</a></li>
1458 // The goal is to return a reasonable title for the outer container div, because
1459 // it's focusable - but without making its title be the full inner text, which is
1460 // quite long. As a heuristic, skip links, controls, and elements that are usually
1461 // containers with lots of children.
1463 // Skip focusable children, so we don't include the text of links and controls.
1464 if (obj->canSetFocusAttribute())
1467 // Skip big container elements like lists, tables, etc.
1468 if (obj->isList() || obj->isAccessibilityTable() || obj->isTree() || obj->isCanvas())
1474 String AccessibilityNodeObject::textUnderElement() const
1476 Node* node = this->node();
1477 if (node && node->isTextNode())
1478 return toText(node)->wholeText();
1481 for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
1482 if (!shouldUseAccessiblityObjectInnerText(child))
1485 if (child->isAccessibilityNodeObject()) {
1486 Vector<AccessibilityText> textOrder;
1487 toAccessibilityNodeObject(child)->alternativeText(textOrder);
1488 if (textOrder.size() > 0) {
1489 result.append(textOrder[0].text);
1494 result.append(child->textUnderElement());
1500 String AccessibilityNodeObject::title() const
1502 Node* node = this->node();
1506 bool isInputTag = node->hasTagName(inputTag);
1508 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1509 if (input->isTextButton())
1510 return input->valueWithDefault();
1513 if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1514 HTMLLabelElement* label = labelForElement(toElement(node));
1515 if (label && !exposesTitleUIElement())
1516 return label->innerText();
1519 // If this node isn't rendered, there's no inner text we can extract from a select element.
1520 if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1523 switch (roleValue()) {
1524 case PopUpButtonRole:
1525 // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1526 if (node->hasTagName(selectTag))
1529 case ToggleButtonRole:
1531 case ListBoxOptionRole:
1532 case MenuButtonRole:
1534 case RadioButtonRole:
1536 return textUnderElement();
1537 // SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
1544 if (isHeading() || isLink())
1545 return textUnderElement();
1547 // If it's focusable but it's not content editable or a known control type, then it will appear to
1548 // the user as a single atomic object, so we should use its text as the default title.
1549 if (isGenericFocusableElement())
1550 return textUnderElement();
1555 String AccessibilityNodeObject::text() const
1557 // If this is a user defined static text, use the accessible name computation.
1558 if (ariaRoleAttribute() == StaticTextRole)
1559 return ariaAccessibilityDescription();
1561 if (!isTextControl())
1564 Node* node = this->node();
1568 if (isNativeTextControl()) {
1569 if (node->hasTagName(textareaTag))
1570 return static_cast<HTMLTextAreaElement*>(node)->value();
1571 if (node->hasTagName(inputTag))
1572 return node->toInputElement()->value();
1575 if (!node->isElementNode())
1578 return toElement(node)->innerText();
1581 String AccessibilityNodeObject::stringValue() const
1583 Node* node = this->node();
1587 if (ariaRoleAttribute() == StaticTextRole) {
1588 String staticText = text();
1589 if (!staticText.length())
1590 staticText = textUnderElement();
1594 if (node->isTextNode())
1595 return textUnderElement();
1597 if (node->hasTagName(selectTag)) {
1598 HTMLSelectElement* selectElement = toHTMLSelectElement(node);
1599 int selectedIndex = selectElement->selectedIndex();
1600 const Vector<HTMLElement*> listItems = selectElement->listItems();
1601 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
1602 const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
1603 if (!overriddenDescription.isNull())
1604 return overriddenDescription;
1606 if (!selectElement->multiple())
1607 return selectElement->value();
1611 if (isTextControl())
1614 // FIXME: We might need to implement a value here for more types
1615 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
1616 // this would require subclassing or making accessibilityAttributeNames do something other than return a
1617 // single static array.
1621 void AccessibilityNodeObject::colorValue(int& r, int& g, int& b) const
1630 if (!node() || !node()->hasTagName(inputTag))
1633 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
1634 const AtomicString& type = input->getAttribute(typeAttr);
1635 if (!equalIgnoringCase(type, "color"))
1638 // HTMLInputElement::value always returns a string parseable by Color().
1639 Color color(input->value());
1645 // This function implements the ARIA accessible name as described by the Mozilla
1646 // ARIA Implementer's Guide.
1647 static String accessibleNameForNode(Node* node)
1649 if (node->isTextNode())
1650 return toText(node)->data();
1652 if (node->hasTagName(inputTag))
1653 return static_cast<HTMLInputElement*>(node)->value();
1655 if (node->isHTMLElement()) {
1656 const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
1664 String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
1666 StringBuilder builder;
1667 unsigned size = elements.size();
1668 for (unsigned i = 0; i < size; ++i) {
1669 Element* idElement = elements[i];
1671 builder.append(accessibleNameForNode(idElement));
1672 for (Node* n = idElement->firstChild(); n; n = NodeTraversal::next(n, idElement))
1673 builder.append(accessibleNameForNode(n));
1676 builder.append(' ');
1678 return builder.toString();
1681 String AccessibilityNodeObject::ariaDescribedByAttribute() const
1683 Vector<Element*> elements;
1684 elementsFromAttribute(elements, aria_describedbyAttr);
1686 return accessibilityDescriptionForElements(elements);
1689 void AccessibilityNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
1691 Node* node = this->node();
1692 if (!node || !node->isElementNode())
1695 TreeScope* scope = node->treeScope();
1699 String idList = getAttribute(attribute).string();
1700 if (idList.isEmpty())
1703 idList.replace('\n', ' ');
1704 Vector<String> idVector;
1705 idList.split(' ', idVector);
1707 unsigned size = idVector.size();
1708 for (unsigned i = 0; i < size; ++i) {
1709 AtomicString idName(idVector[i]);
1710 Element* idElement = scope->getElementById(idName);
1712 elements.append(idElement);
1717 void AccessibilityNodeObject::ariaLabeledByElements(Vector<Element*>& elements) const
1719 elementsFromAttribute(elements, aria_labeledbyAttr);
1720 if (!elements.size())
1721 elementsFromAttribute(elements, aria_labelledbyAttr);
1725 String AccessibilityNodeObject::ariaLabeledByAttribute() const
1727 Vector<Element*> elements;
1728 ariaLabeledByElements(elements);
1730 return accessibilityDescriptionForElements(elements);
1733 bool AccessibilityNodeObject::canSetFocusAttribute() const
1735 Node* node = this->node();
1742 // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
1743 // do anything. For example, setFocusedNode() will do nothing if the current focused
1744 // node will not relinquish the focus.
1748 if (node->isElementNode() && !toElement(node)->isEnabledFormControl())
1751 return node->supportsFocus();
1754 AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
1756 const AtomicString& ariaRole = getAttribute(roleAttr);
1757 if (ariaRole.isNull() || ariaRole.isEmpty())
1760 AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
1762 // ARIA states if an item can get focus, it should not be presentational.
1763 if (role == PresentationalRole && canSetFocusAttribute())
1766 if (role == ButtonRole)
1767 role = buttonRoleType();
1769 if (role == TextAreaRole && !ariaIsMultiline())
1770 role = TextFieldRole;
1772 role = remapAriaRoleDueToParent(role);
1780 AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
1785 AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
1787 // Some objects change their role based on their parent.
1788 // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop.
1789 // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
1790 // https://bugs.webkit.org/show_bug.cgi?id=65174
1792 if (role != ListBoxOptionRole && role != MenuItemRole)
1795 for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
1796 AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
1798 // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
1799 if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
1800 return MenuItemRole;
1801 // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
1802 if (role == MenuItemRole && parentAriaRole == GroupRole)
1803 return MenuButtonRole;
1805 // If the parent had a different role, then we don't need to continue searching up the chain.
1813 // If you call node->rendererIsEditable() since that will return true if an ancestor is editable.
1814 // This only returns true if this is the element that actually has the contentEditable attribute set.
1815 bool AccessibilityNodeObject::hasContentEditableAttributeSet() const
1817 if (!hasAttribute(contenteditableAttr))
1819 const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
1820 // Both "true" (case-insensitive) and the empty string count as true.
1821 return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
1824 } // namespace WebCore