This source file includes following definitions.
- m_node
 
- create
 
- accessibleNameForNode
 
- accessibilityDescriptionForElements
 
- alterSliderValue
 
- ariaAccessibilityDescription
 
- ariaLabeledByElements
 
- changeValueByStep
 
- computeAccessibilityIsIgnored
 
- determineAccessibilityRole
 
- determineAriaRoleAttribute
 
- elementsFromAttribute
 
- hasContentEditableAttributeSet
 
- isDescendantOfBarrenParent
 
- isGenericFocusableElement
 
- labelForElement
 
- menuButtonForMenu
 
- siblingWithAriaRole
 
- menuItemElementForMenu
 
- mouseButtonListener
 
- remapAriaRoleDueToParent
 
- init
 
- detach
 
- isAnchor
 
- isControl
 
- isFieldset
 
- isHeading
 
- isHovered
 
- isImage
 
- isImageButton
 
- isInputImage
 
- isLink
 
- isMenu
 
- isMenuButton
 
- isMultiSelectable
 
- isNativeCheckboxOrRadio
 
- isNativeImage
 
- isNativeTextControl
 
- isNonNativeTextControl
 
- isPasswordField
 
- isProgressIndicator
 
- isSlider
 
- isChecked
 
- isClickable
 
- isEnabled
 
- isIndeterminate
 
- isPressed
 
- isReadOnly
 
- isRequired
 
- canSetFocusAttribute
 
- canSetValueAttribute
 
- canvasHasFallbackContent
 
- exposesTitleUIElement
 
- headingLevel
 
- hierarchicalLevel
 
- text
 
- titleUIElement
 
- checkboxOrRadioValue
 
- colorValue
 
- valueDescription
 
- valueForRange
 
- maxValueForRange
 
- minValueForRange
 
- stepValueForRange
 
- stringValue
 
- ariaDescribedByAttribute
 
- ariaLabeledByAttribute
 
- ariaRoleAttribute
 
- shouldUseAccessiblityObjectInnerText
 
- textUnderElement
 
- accessibilityDescription
 
- title
 
- helpText
 
- elementRect
 
- parentObject
 
- parentObjectIfExists
 
- firstChild
 
- nextSibling
 
- addChildren
 
- addChild
 
- insertChild
 
- canHaveChildren
 
- actionElement
 
- anchorElement
 
- document
 
- setNode
 
- correspondingControlForLabelElement
 
- labelElementContainer
 
- setFocused
 
- increment
 
- decrement
 
- childrenChanged
 
- selectionChanged
 
- textChanged
 
- updateAccessibilityRole
 
- alternativeTextForWebArea
 
- alternativeText
 
- ariaLabeledByText
 
- changeValueByPercent
 
#include "config.h"
#include "core/accessibility/AXNodeObject.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/Text.h"
#include "core/html/HTMLFieldSetElement.h"
#include "core/html/HTMLFrameElementBase.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLLabelElement.h"
#include "core/html/HTMLLegendElement.h"
#include "core/html/HTMLSelectElement.h"
#include "core/html/HTMLTextAreaElement.h"
#include "core/rendering/RenderObject.h"
#include "platform/UserGestureIndicator.h"
#include "wtf/text/StringBuilder.h"
namespace WebCore {
using namespace HTMLNames;
AXNodeObject::AXNodeObject(Node* node)
    : AXObject()
    , m_ariaRole(UnknownRole)
    , m_childrenDirty(false)
#ifndef NDEBUG
    , m_initialized(false)
#endif
    , m_node(node)
{
}
PassRefPtr<AXNodeObject> AXNodeObject::create(Node* node)
{
    return adoptRef(new AXNodeObject(node));
}
AXNodeObject::~AXNodeObject()
{
    ASSERT(isDetached());
}
static String accessibleNameForNode(Node* node)
{
    if (node->isTextNode())
        return toText(node)->data();
    if (isHTMLInputElement(*node))
        return toHTMLInputElement(*node).value();
    if (node->isHTMLElement()) {
        const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
        if (!alt.isEmpty())
            return alt;
    }
    return String();
}
String AXNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
{
    StringBuilder builder;
    unsigned size = elements.size();
    for (unsigned i = 0; i < size; ++i) {
        Element* idElement = elements[i];
        builder.append(accessibleNameForNode(idElement));
        for (Node* n = idElement->firstChild(); n; n = NodeTraversal::next(*n, idElement))
            builder.append(accessibleNameForNode(n));
        if (i != size - 1)
            builder.append(' ');
    }
    return builder.toString();
}
void AXNodeObject::alterSliderValue(bool increase)
{
    if (roleValue() != SliderRole)
        return;
    if (!getAttribute(stepAttr).isEmpty())
        changeValueByStep(increase);
    else
        changeValueByPercent(increase ? 5 : -5);
}
String AXNodeObject::ariaAccessibilityDescription() const
{
    String ariaLabeledBy = ariaLabeledByAttribute();
    if (!ariaLabeledBy.isEmpty())
        return ariaLabeledBy;
    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
    if (!ariaLabel.isEmpty())
        return ariaLabel;
    return String();
}
void AXNodeObject::ariaLabeledByElements(Vector<Element*>& elements) const
{
    elementsFromAttribute(elements, aria_labeledbyAttr);
    if (!elements.size())
        elementsFromAttribute(elements, aria_labelledbyAttr);
}
void AXNodeObject::changeValueByStep(bool increase)
{
    float step = stepValueForRange();
    float value = valueForRange();
    value += increase ? step : -step;
    setValue(String::number(value));
    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
}
bool AXNodeObject::computeAccessibilityIsIgnored() const
{
#ifndef NDEBUG
    
    
    ASSERT(m_initialized);
#endif
    
    if (isDescendantOfBarrenParent())
        return true;
    
    AXObject* controlObject = correspondingControlForLabelElement();
    if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
        return true;
    return m_role == UnknownRole;
}
AccessibilityRole AXNodeObject::determineAccessibilityRole()
{
    if (!node())
        return UnknownRole;
    m_ariaRole = determineAriaRoleAttribute();
    AccessibilityRole ariaRole = ariaRoleAttribute();
    if (ariaRole != UnknownRole)
        return ariaRole;
    if (node()->isLink())
        return LinkRole;
    if (node()->isTextNode())
        return StaticTextRole;
    if (isHTMLButtonElement(*node()))
        return buttonRoleType();
    if (isHTMLInputElement(*node())) {
        HTMLInputElement& input = toHTMLInputElement(*node());
        if (input.isCheckbox())
            return CheckBoxRole;
        if (input.isRadioButton())
            return RadioButtonRole;
        if (input.isTextButton())
            return buttonRoleType();
        if (input.isRangeControl())
            return SliderRole;
        const AtomicString& type = input.getAttribute(typeAttr);
        if (equalIgnoringCase(type, "color"))
            return ColorWellRole;
        return TextFieldRole;
    }
    if (isHTMLSelectElement(*node())) {
        HTMLSelectElement& selectElement = toHTMLSelectElement(*node());
        return selectElement.multiple() ? ListBoxRole : PopUpButtonRole;
    }
    if (isHTMLTextAreaElement(*node()))
        return TextAreaRole;
    if (headingLevel())
        return HeadingRole;
    if (isHTMLDivElement(*node()))
        return DivRole;
    if (isHTMLParagraphElement(*node()))
        return ParagraphRole;
    if (isHTMLLabelElement(*node()))
        return LabelRole;
    if (node()->isElementNode() && toElement(node())->isFocusable())
        return GroupRole;
    if (isHTMLAnchorElement(*node()) && isClickable())
        return LinkRole;
    if (node()->hasTagName(iframeTag))
        return IframeRole;
    return UnknownRole;
}
AccessibilityRole AXNodeObject::determineAriaRoleAttribute() const
{
    const AtomicString& ariaRole = getAttribute(roleAttr);
    if (ariaRole.isNull() || ariaRole.isEmpty())
        return UnknownRole;
    AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
    
    if (role == PresentationalRole && canSetFocusAttribute())
        return UnknownRole;
    if (role == ButtonRole)
        role = buttonRoleType();
    if (role == TextAreaRole && !ariaIsMultiline())
        role = TextFieldRole;
    role = remapAriaRoleDueToParent(role);
    if (role)
        return role;
    return UnknownRole;
}
void AXNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
{
    Node* node = this->node();
    if (!node || !node->isElementNode())
        return;
    TreeScope& scope = node->treeScope();
    String idList = getAttribute(attribute).string();
    if (idList.isEmpty())
        return;
    idList.replace('\n', ' ');
    Vector<String> idVector;
    idList.split(' ', idVector);
    unsigned size = idVector.size();
    for (unsigned i = 0; i < size; ++i) {
        AtomicString idName(idVector[i]);
        Element* idElement = scope.getElementById(idName);
        if (idElement)
            elements.append(idElement);
    }
}
bool AXNodeObject::hasContentEditableAttributeSet() const
{
    if (!hasAttribute(contenteditableAttr))
        return false;
    const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
    
    return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
}
bool AXNodeObject::isDescendantOfBarrenParent() const
{
    for (AXObject* object = parentObject(); object; object = object->parentObject()) {
        if (!object->canHaveChildren())
            return true;
    }
    return false;
}
bool AXNodeObject::isGenericFocusableElement() const
{
    if (!canSetFocusAttribute())
        return false;
    
    if (isControl())
        return false;
    
    if (m_ariaRole != UnknownRole)
        return false;
    
    
    
    if (hasContentEditableAttributeSet())
        return false;
    
    
    if (roleValue() == WebAreaRole)
        return false;
    if (isHTMLBodyElement(node()))
        return false;
    
    
    if (roleValue() == SVGRootRole)
        return false;
    return true;
}
HTMLLabelElement* AXNodeObject::labelForElement(Element* element) const
{
    if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
        return 0;
    const AtomicString& id = element->getIdAttribute();
    if (!id.isEmpty()) {
        if (HTMLLabelElement* label = element->treeScope().labelElementForId(id))
            return label;
    }
    for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) {
        if (isHTMLLabelElement(*parent))
            return toHTMLLabelElement(parent);
    }
    return 0;
}
AXObject* AXNodeObject::menuButtonForMenu() const
{
    Element* menuItem = menuItemElementForMenu();
    if (menuItem) {
        
        AXObject* menuItemAX = axObjectCache()->getOrCreate(menuItem);
        if (menuItemAX && menuItemAX->isMenuButton())
            return menuItemAX;
    }
    return 0;
}
static Element* siblingWithAriaRole(String role, Node* node)
{
    Node* parent = node->parentNode();
    if (!parent)
        return 0;
    for (Element* sibling = ElementTraversal::firstChild(*parent); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
        const AtomicString& siblingAriaRole = sibling->getAttribute(roleAttr);
        if (equalIgnoringCase(siblingAriaRole, role))
            return sibling;
    }
    return 0;
}
Element* AXNodeObject::menuItemElementForMenu() const
{
    if (ariaRoleAttribute() != MenuRole)
        return 0;
    return siblingWithAriaRole("menuitem", node());
}
Element* AXNodeObject::mouseButtonListener() const
{
    Node* node = this->node();
    if (!node)
        return 0;
    
    while (node && !node->isElementNode())
        node = node->parentNode();
    if (!node)
        return 0;
    
    for (Element* element = toElement(node); element; element = element->parentElement()) {
        if (element->getAttributeEventListener(EventTypeNames::click) || element->getAttributeEventListener(EventTypeNames::mousedown) || element->getAttributeEventListener(EventTypeNames::mouseup))
            return element;
    }
    return 0;
}
AccessibilityRole AXNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
{
    
    
    
    
    if (role != ListBoxOptionRole && role != MenuItemRole)
        return role;
    for (AXObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
        AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
        
        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
            return MenuItemRole;
        
        if (role == MenuItemRole && parentAriaRole == GroupRole)
            return MenuButtonRole;
        
        if (parentAriaRole)
            break;
    }
    return role;
}
void AXNodeObject::init()
{
#ifndef NDEBUG
    ASSERT(!m_initialized);
    m_initialized = true;
#endif
    m_role = determineAccessibilityRole();
}
void AXNodeObject::detach()
{
    clearChildren();
    AXObject::detach();
    m_node = 0;
}
bool AXNodeObject::isAnchor() const
{
    return !isNativeImage() && isLink();
}
bool AXNodeObject::isControl() const
{
    Node* node = this->node();
    if (!node)
        return false;
    return ((node->isElementNode() && toElement(node)->isFormControlElement())
        || AXObject::isARIAControl(ariaRoleAttribute()));
}
bool AXNodeObject::isFieldset() const
{
    return isHTMLFieldSetElement(node());
}
bool AXNodeObject::isHeading() const
{
    return roleValue() == HeadingRole;
}
bool AXNodeObject::isHovered() const
{
    Node* node = this->node();
    if (!node)
        return false;
    return node->hovered();
}
bool AXNodeObject::isImage() const
{
    return roleValue() == ImageRole;
}
bool AXNodeObject::isImageButton() const
{
    return isNativeImage() && isButton();
}
bool AXNodeObject::isInputImage() const
{
    Node* node = this->node();
    if (roleValue() == ButtonRole && isHTMLInputElement(node))
        return toHTMLInputElement(*node).isImageButton();
    return false;
}
bool AXNodeObject::isLink() const
{
    return roleValue() == LinkRole;
}
bool AXNodeObject::isMenu() const
{
    return roleValue() == MenuRole;
}
bool AXNodeObject::isMenuButton() const
{
    return roleValue() == MenuButtonRole;
}
bool AXNodeObject::isMultiSelectable() const
{
    const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
    if (equalIgnoringCase(ariaMultiSelectable, "true"))
        return true;
    if (equalIgnoringCase(ariaMultiSelectable, "false"))
        return false;
    return isHTMLSelectElement(node()) && toHTMLSelectElement(*node()).multiple();
}
bool AXNodeObject::isNativeCheckboxOrRadio() const
{
    Node* node = this->node();
    if (!isHTMLInputElement(node))
        return false;
    HTMLInputElement* input = toHTMLInputElement(node);
    return input->isCheckbox() || input->isRadioButton();
}
bool AXNodeObject::isNativeImage() const
{
    Node* node = this->node();
    if (!node)
        return false;
    if (isHTMLImageElement(*node))
        return true;
    if (isHTMLAppletElement(*node) || isHTMLEmbedElement(*node) || isHTMLObjectElement(*node))
        return true;
    if (isHTMLInputElement(*node))
        return toHTMLInputElement(*node).isImageButton();
    return false;
}
bool AXNodeObject::isNativeTextControl() const
{
    Node* node = this->node();
    if (!node)
        return false;
    if (isHTMLTextAreaElement(*node))
        return true;
    if (isHTMLInputElement(*node)) {
        HTMLInputElement* input = toHTMLInputElement(node);
        return input->isText() || input->isNumberField();
    }
    return false;
}
bool AXNodeObject::isNonNativeTextControl() const
{
    if (isNativeTextControl())
        return false;
    if (hasContentEditableAttributeSet())
        return true;
    if (isARIATextControl())
        return true;
    return false;
}
bool AXNodeObject::isPasswordField() const
{
    Node* node = this->node();
    if (!isHTMLInputElement(node))
        return false;
    if (ariaRoleAttribute() != UnknownRole)
        return false;
    return toHTMLInputElement(node)->isPasswordField();
}
bool AXNodeObject::isProgressIndicator() const
{
    return roleValue() == ProgressIndicatorRole;
}
bool AXNodeObject::isSlider() const
{
    return roleValue() == SliderRole;
}
bool AXNodeObject::isChecked() const
{
    Node* node = this->node();
    if (!node)
        return false;
    
    if (isHTMLInputElement(*node))
        return toHTMLInputElement(*node).shouldAppearChecked();
    
    AccessibilityRole ariaRole = ariaRoleAttribute();
    if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
        if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
            return true;
        return false;
    }
    
    return false;
}
bool AXNodeObject::isClickable() const
{
    if (node()) {
        if (node()->isElementNode() && toElement(node())->isDisabledFormControl())
            return false;
        
        if (node()->hasEventListeners(EventTypeNames::mouseup) || node()->hasEventListeners(EventTypeNames::mousedown) || node()->hasEventListeners(EventTypeNames::click) || node()->hasEventListeners(EventTypeNames::DOMActivate))
            return true;
    }
    return AXObject::isClickable();
}
bool AXNodeObject::isEnabled() const
{
    if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
        return false;
    Node* node = this->node();
    if (!node || !node->isElementNode())
        return true;
    return !toElement(node)->isDisabledFormControl();
}
bool AXNodeObject::isIndeterminate() const
{
    Node* node = this->node();
    if (!isHTMLInputElement(node))
        return false;
    return toHTMLInputElement(node)->shouldAppearIndeterminate();
}
bool AXNodeObject::isPressed() const
{
    if (!isButton())
        return false;
    Node* node = this->node();
    if (!node)
        return false;
    
    if (ariaRoleAttribute() == ButtonRole) {
        if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
            return true;
        return false;
    }
    return node->active();
}
bool AXNodeObject::isReadOnly() const
{
    Node* node = this->node();
    if (!node)
        return true;
    if (isHTMLTextAreaElement(*node))
        return toHTMLTextAreaElement(*node).isReadOnly();
    if (isHTMLInputElement(*node)) {
        HTMLInputElement& input = toHTMLInputElement(*node);
        if (input.isTextField())
            return input.isReadOnly();
    }
    return !node->rendererIsEditable();
}
bool AXNodeObject::isRequired() const
{
    if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
        return true;
    Node* n = this->node();
    if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
        return toHTMLFormControlElement(n)->isRequired();
    return false;
}
bool AXNodeObject::canSetFocusAttribute() const
{
    Node* node = this->node();
    if (!node)
        return false;
    if (isWebArea())
        return true;
    
    
    
    if (!node)
        return false;
    if (isDisabledFormControl(node))
        return false;
    return node->isElementNode() && toElement(node)->supportsFocus();
}
bool AXNodeObject::canSetValueAttribute() const
{
    if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true"))
        return false;
    if (isProgressIndicator() || isSlider())
        return true;
    if (isTextControl() && !isNativeTextControl())
        return true;
    
    
    return !isReadOnly();
}
bool AXNodeObject::canvasHasFallbackContent() const
{
    Node* node = this->node();
    if (!isHTMLCanvasElement(node))
        return false;
    
    
    
    return ElementTraversal::firstChild(*node);
}
bool AXNodeObject::exposesTitleUIElement() const
{
    if (!isControl())
        return false;
    
    
    if (accessibilityIsIgnored())
        return true;
    
    
    bool hasTextAlternative = (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).isEmpty());
    
    
    
    if (isCheckboxOrRadio())
        return hasTextAlternative;
    
    if (hasTextAlternative)
        return false;
    return true;
}
int AXNodeObject::headingLevel() const
{
    
    Node* node = this->node();
    if (!node)
        return false;
    if (ariaRoleAttribute() == HeadingRole)
        return getAttribute(aria_levelAttr).toInt();
    if (node->hasTagName(h1Tag))
        return 1;
    if (node->hasTagName(h2Tag))
        return 2;
    if (node->hasTagName(h3Tag))
        return 3;
    if (node->hasTagName(h4Tag))
        return 4;
    if (node->hasTagName(h5Tag))
        return 5;
    if (node->hasTagName(h6Tag))
        return 6;
    return 0;
}
unsigned AXNodeObject::hierarchicalLevel() const
{
    Node* node = this->node();
    if (!node || !node->isElementNode())
        return 0;
    Element* element = toElement(node);
    String ariaLevel = element->getAttribute(aria_levelAttr);
    if (!ariaLevel.isEmpty())
        return ariaLevel.toInt();
    
    if (roleValue() != TreeItemRole)
        return 0;
    
    
    unsigned level = 1;
    for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
        AccessibilityRole parentRole = parent->roleValue();
        if (parentRole == GroupRole)
            level++;
        else if (parentRole == TreeRole)
            break;
    }
    return level;
}
String AXNodeObject::text() const
{
    
    if (ariaRoleAttribute() == StaticTextRole)
        return ariaAccessibilityDescription();
    if (!isTextControl())
        return String();
    Node* node = this->node();
    if (!node)
        return String();
    if (isNativeTextControl() && (isHTMLTextAreaElement(*node) || isHTMLInputElement(*node)))
        return toHTMLTextFormControlElement(*node).value();
    if (!node->isElementNode())
        return String();
    return toElement(node)->innerText();
}
AXObject* AXNodeObject::titleUIElement() const
{
    if (!node() || !node()->isElementNode())
        return 0;
    if (isFieldset())
        return axObjectCache()->getOrCreate(toHTMLFieldSetElement(node())->legend());
    HTMLLabelElement* label = labelForElement(toElement(node()));
    if (label)
        return axObjectCache()->getOrCreate(label);
    return 0;
}
AccessibilityButtonState AXNodeObject::checkboxOrRadioValue() const
{
    if (isNativeCheckboxOrRadio())
        return isChecked() ? ButtonStateOn : ButtonStateOff;
    return AXObject::checkboxOrRadioValue();
}
void AXNodeObject::colorValue(int& r, int& g, int& b) const
{
    r = 0;
    g = 0;
    b = 0;
    if (!isColorWell())
        return;
    if (!isHTMLInputElement(node()))
        return;
    HTMLInputElement* input = toHTMLInputElement(node());
    const AtomicString& type = input->getAttribute(typeAttr);
    if (!equalIgnoringCase(type, "color"))
        return;
    
    Color color;
    bool success = color.setFromString(input->value());
    ASSERT_UNUSED(success, success);
    r = color.red();
    g = color.green();
    b = color.blue();
}
String AXNodeObject::valueDescription() const
{
    if (!supportsRangeValue())
        return String();
    return getAttribute(aria_valuetextAttr).string();
}
float AXNodeObject::valueForRange() const
{
    if (hasAttribute(aria_valuenowAttr))
        return getAttribute(aria_valuenowAttr).toFloat();
    if (isHTMLInputElement(node())) {
        HTMLInputElement& input = toHTMLInputElement(*node());
        if (input.isRangeControl())
            return input.valueAsNumber();
    }
    return 0.0;
}
float AXNodeObject::maxValueForRange() const
{
    if (hasAttribute(aria_valuemaxAttr))
        return getAttribute(aria_valuemaxAttr).toFloat();
    if (isHTMLInputElement(node())) {
        HTMLInputElement& input = toHTMLInputElement(*node());
        if (input.isRangeControl())
            return input.maximum();
    }
    return 0.0;
}
float AXNodeObject::minValueForRange() const
{
    if (hasAttribute(aria_valueminAttr))
        return getAttribute(aria_valueminAttr).toFloat();
    if (isHTMLInputElement(node())) {
        HTMLInputElement& input = toHTMLInputElement(*node());
        if (input.isRangeControl())
            return input.minimum();
    }
    return 0.0;
}
float AXNodeObject::stepValueForRange() const
{
    return getAttribute(stepAttr).toFloat();
}
String AXNodeObject::stringValue() const
{
    Node* node = this->node();
    if (!node)
        return String();
    if (ariaRoleAttribute() == StaticTextRole) {
        String staticText = text();
        if (!staticText.length())
            staticText = textUnderElement();
        return staticText;
    }
    if (node->isTextNode())
        return textUnderElement();
    if (isHTMLSelectElement(*node)) {
        HTMLSelectElement& selectElement = toHTMLSelectElement(*node);
        int selectedIndex = selectElement.selectedIndex();
        const Vector<HTMLElement*> listItems = selectElement.listItems();
        if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
            const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
            if (!overriddenDescription.isNull())
                return overriddenDescription;
        }
        if (!selectElement.multiple())
            return selectElement.value();
        return String();
    }
    if (isTextControl())
        return text();
    
    
    
    
    return String();
}
String AXNodeObject::ariaDescribedByAttribute() const
{
    Vector<Element*> elements;
    elementsFromAttribute(elements, aria_describedbyAttr);
    return accessibilityDescriptionForElements(elements);
}
String AXNodeObject::ariaLabeledByAttribute() const
{
    Vector<Element*> elements;
    ariaLabeledByElements(elements);
    return accessibilityDescriptionForElements(elements);
}
AccessibilityRole AXNodeObject::ariaRoleAttribute() const
{
    return m_ariaRole;
}
static bool shouldUseAccessiblityObjectInnerText(AXObject* obj)
{
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    if (obj->isInertOrAriaHidden())
        return false;
    
    if (obj->canSetFocusAttribute())
        return false;
    
    if (obj->isList() || obj->isAXTable() || obj->isTree() || obj->isCanvas())
        return false;
    return true;
}
String AXNodeObject::textUnderElement() const
{
    Node* node = this->node();
    if (node && node->isTextNode())
        return toText(node)->wholeText();
    StringBuilder builder;
    for (AXObject* child = firstChild(); child; child = child->nextSibling()) {
        if (!shouldUseAccessiblityObjectInnerText(child))
            continue;
        if (child->isAXNodeObject()) {
            Vector<AccessibilityText> textOrder;
            toAXNodeObject(child)->alternativeText(textOrder);
            if (textOrder.size() > 0) {
                builder.append(textOrder[0].text);
                continue;
            }
        }
        builder.append(child->textUnderElement());
    }
    return builder.toString();
}
String AXNodeObject::accessibilityDescription() const
{
    
    if (roleValue() == StaticTextRole)
        return String();
    String ariaDescription = ariaAccessibilityDescription();
    if (!ariaDescription.isEmpty())
        return ariaDescription;
    if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
        
        
        const AtomicString& alt = getAttribute(altAttr);
        if (!alt.isNull())
            return alt;
    }
    
    
    
    
    if (title().isEmpty())
        return getAttribute(titleAttr);
    return String();
}
String AXNodeObject::title() const
{
    Node* node = this->node();
    if (!node)
        return String();
    bool isInputElement = isHTMLInputElement(*node);
    if (isInputElement) {
        HTMLInputElement& input = toHTMLInputElement(*node);
        if (input.isTextButton())
            return input.valueWithDefault();
    }
    if (isInputElement || AXObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
        HTMLLabelElement* label = labelForElement(toElement(node));
        if (label && !exposesTitleUIElement())
            return label->innerText();
    }
    
    if (!isAXRenderObject() && isHTMLSelectElement(*node))
        return String();
    switch (roleValue()) {
    case PopUpButtonRole:
        
        if (isHTMLSelectElement(*node))
            return String();
    case ButtonRole:
    case ToggleButtonRole:
    case CheckBoxRole:
    case ListBoxOptionRole:
    case MenuButtonRole:
    case MenuItemRole:
    case RadioButtonRole:
    case TabRole:
        return textUnderElement();
    
    case SVGRootRole:
        return String();
    default:
        break;
    }
    if (isHeading() || isLink())
        return textUnderElement();
    
    
    if (isGenericFocusableElement())
        return textUnderElement();
    return String();
}
String AXNodeObject::helpText() const
{
    Node* node = this->node();
    if (!node)
        return String();
    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
    if (!ariaHelp.isEmpty())
        return ariaHelp;
    String describedBy = ariaDescribedByAttribute();
    if (!describedBy.isEmpty())
        return describedBy;
    String description = accessibilityDescription();
    for (Node* curr = node; curr; curr = curr->parentNode()) {
        if (curr->isHTMLElement()) {
            const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
            if (!summary.isEmpty())
                return summary;
            
            const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
            if (!title.isEmpty() && description != title)
                return title;
        }
        
        
        AXObject* axObj = axObjectCache()->getOrCreate(curr);
        if (axObj) {
            AccessibilityRole role = axObj->roleValue();
            if (role != GroupRole && role != UnknownRole)
                break;
        }
    }
    return String();
}
LayoutRect AXNodeObject::elementRect() const
{
    
    if (!m_explicitElementRect.isEmpty())
        return m_explicitElementRect;
    
    
    
    LayoutRect boundingBox;
    for (AXObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
        if (positionProvider->isAXRenderObject()) {
            LayoutRect parentRect = positionProvider->elementRect();
            boundingBox.setSize(LayoutSize(parentRect.width(), LayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
            boundingBox.setLocation(parentRect.location());
            break;
        }
    }
    return boundingBox;
}
AXObject* AXNodeObject::parentObject() const
{
    if (!node())
        return 0;
    Node* parentObj = node()->parentNode();
    if (parentObj)
        return axObjectCache()->getOrCreate(parentObj);
    return 0;
}
AXObject* AXNodeObject::parentObjectIfExists() const
{
    return parentObject();
}
AXObject* AXNodeObject::firstChild() const
{
    if (!node())
        return 0;
    Node* firstChild = node()->firstChild();
    if (!firstChild)
        return 0;
    return axObjectCache()->getOrCreate(firstChild);
}
AXObject* AXNodeObject::nextSibling() const
{
    if (!node())
        return 0;
    Node* nextSibling = node()->nextSibling();
    if (!nextSibling)
        return 0;
    return axObjectCache()->getOrCreate(nextSibling);
}
void AXNodeObject::addChildren()
{
    
    
    ASSERT(!m_haveChildren);
    if (!m_node)
        return;
    m_haveChildren = true;
    
    if (renderer() && !isHTMLCanvasElement(*m_node))
        return;
    for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
        addChild(axObjectCache()->getOrCreate(child));
}
void AXNodeObject::addChild(AXObject* child)
{
    insertChild(child, m_children.size());
}
void AXNodeObject::insertChild(AXObject* child, unsigned index)
{
    if (!child)
        return;
    
    
    
    child->clearChildren();
    if (child->accessibilityIsIgnored()) {
        AccessibilityChildrenVector children = child->children();
        size_t length = children.size();
        for (size_t i = 0; i < length; ++i)
            m_children.insert(index + i, children[i]);
    } else {
        ASSERT(child->parentObject() == this);
        m_children.insert(index, child);
    }
}
bool AXNodeObject::canHaveChildren() const
{
    
    
    
    if (!node() && !isAXRenderObject())
        return false;
    
    switch (roleValue()) {
    case ImageRole:
    case ButtonRole:
    case PopUpButtonRole:
    case CheckBoxRole:
    case RadioButtonRole:
    case TabRole:
    case ToggleButtonRole:
    case ListBoxOptionRole:
    case ScrollBarRole:
        return false;
    case StaticTextRole:
        if (!axObjectCache()->inlineTextBoxAccessibility())
            return false;
    default:
        return true;
    }
}
Element* AXNodeObject::actionElement() const
{
    Node* node = this->node();
    if (!node)
        return 0;
    if (isHTMLInputElement(*node)) {
        HTMLInputElement& input = toHTMLInputElement(*node);
        if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTextButton()))
            return &input;
    } else if (isHTMLButtonElement(*node)) {
        return toElement(node);
    }
    if (isFileUploadButton())
        return toElement(node);
    if (AXObject::isARIAInput(ariaRoleAttribute()))
        return toElement(node);
    if (isImageButton())
        return toElement(node);
    if (isHTMLSelectElement(*node))
        return toElement(node);
    switch (roleValue()) {
    case ButtonRole:
    case PopUpButtonRole:
    case ToggleButtonRole:
    case TabRole:
    case MenuItemRole:
    case ListItemRole:
        return toElement(node);
    default:
        break;
    }
    Element* elt = anchorElement();
    if (!elt)
        elt = mouseButtonListener();
    return elt;
}
Element* AXNodeObject::anchorElement() const
{
    Node* node = this->node();
    if (!node)
        return 0;
    AXObjectCache* cache = axObjectCache();
    
    
    for ( ; node; node = node->parentNode()) {
        if (isHTMLAnchorElement(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
            return toElement(node);
    }
    return 0;
}
Document* AXNodeObject::document() const
{
    if (!node())
        return 0;
    return &node()->document();
}
void AXNodeObject::setNode(Node* node)
{
    m_node = node;
}
AXObject* AXNodeObject::correspondingControlForLabelElement() const
{
    HTMLLabelElement* labelElement = labelElementContainer();
    if (!labelElement)
        return 0;
    HTMLElement* correspondingControl = labelElement->control();
    if (!correspondingControl)
        return 0;
    
    
    if (correspondingControl->renderer() && !correspondingControl->renderer()->parent())
        return 0;
    return axObjectCache()->getOrCreate(correspondingControl);
}
HTMLLabelElement* AXNodeObject::labelElementContainer() const
{
    if (!node())
        return 0;
    
    if (isControl())
        return 0;
    
    return Traversal<HTMLLabelElement>::firstAncestorOrSelf(*node());
}
void AXNodeObject::setFocused(bool on)
{
    if (!canSetFocusAttribute())
        return;
    Document* document = this->document();
    if (!on) {
        document->setFocusedElement(nullptr);
    } else {
        Node* node = this->node();
        if (node && node->isElementNode()) {
            
            
            
            if (document->focusedElement() == node)
                document->setFocusedElement(nullptr);
            toElement(node)->focus();
        } else {
            document->setFocusedElement(nullptr);
        }
    }
}
void AXNodeObject::increment()
{
    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
    alterSliderValue(true);
}
void AXNodeObject::decrement()
{
    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
    alterSliderValue(false);
}
void AXNodeObject::childrenChanged()
{
    
    if (!node() && !renderer())
        return;
    axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
    
    
    
    
    for (AXObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
        parent->setNeedsToUpdateChildren();
        
        
        
        if (parent->supportsARIALiveRegion())
            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
        
        
        if (isNonNativeTextControl())
            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
    }
}
void AXNodeObject::selectionChanged()
{
    
    
    
    if (isFocused() || isWebArea())
        axObjectCache()->postNotification(this, document(), AXObjectCache::AXSelectedTextChanged, true);
    else
        AXObject::selectionChanged(); 
}
void AXNodeObject::textChanged()
{
    
    
    AXObjectCache* cache = axObjectCache();
    for (Node* parentNode = node(); parentNode; parentNode = parentNode->parentNode()) {
        AXObject* parent = cache->get(parentNode);
        if (!parent)
            continue;
        if (parent->supportsARIALiveRegion())
            cache->postNotification(parentNode, AXObjectCache::AXLiveRegionChanged, true);
        
        
        if (parent->isNonNativeTextControl())
            cache->postNotification(parentNode, AXObjectCache::AXValueChanged, true);
    }
}
void AXNodeObject::updateAccessibilityRole()
{
    bool ignoredStatus = accessibilityIsIgnored();
    m_role = determineAccessibilityRole();
    
    if (ignoredStatus != accessibilityIsIgnored())
        childrenChanged();
}
String AXNodeObject::alternativeTextForWebArea() const
{
    
    
    
    
    
    
    
    
    
    Document* document = this->document();
    if (!document)
        return String();
    
    if (Element* documentElement = document->documentElement()) {
        const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
        if (!ariaLabel.isEmpty())
            return ariaLabel;
    }
    Node* owner = document->ownerElement();
    if (owner) {
        if (isHTMLFrameElementBase(*owner)) {
            const AtomicString& title = toElement(owner)->getAttribute(titleAttr);
            if (!title.isEmpty())
                return title;
            return toElement(owner)->getNameAttribute();
        }
        if (owner->isHTMLElement())
            return toHTMLElement(owner)->getNameAttribute();
    }
    String documentTitle = document->title();
    if (!documentTitle.isEmpty())
        return documentTitle;
    owner = document->body();
    if (owner && owner->isHTMLElement())
        return toHTMLElement(owner)->getNameAttribute();
    return String();
}
void AXNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
{
    if (isWebArea()) {
        String webAreaText = alternativeTextForWebArea();
        if (!webAreaText.isEmpty())
            textOrder.append(AccessibilityText(webAreaText, AlternativeText));
        return;
    }
    ariaLabeledByText(textOrder);
    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
    if (!ariaLabel.isEmpty())
        textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
    if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
        
        
        const AtomicString& alt = getAttribute(altAttr);
        if (!alt.isNull())
            textOrder.append(AccessibilityText(alt, AlternativeText));
    }
}
void AXNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
{
    String ariaLabeledBy = ariaLabeledByAttribute();
    if (!ariaLabeledBy.isEmpty()) {
        Vector<Element*> elements;
        ariaLabeledByElements(elements);
        unsigned length = elements.size();
        for (unsigned k = 0; k < length; k++) {
            RefPtr<AXObject> axElement = axObjectCache()->getOrCreate(elements[k]);
            textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElement));
        }
    }
}
void AXNodeObject::changeValueByPercent(float percentChange)
{
    float range = maxValueForRange() - minValueForRange();
    float value = valueForRange();
    value += range * (percentChange / 100);
    setValue(String::number(value));
    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
}
}