1/*
2* Copyright (C) 2012, Google Inc. All rights reserved.
3*
4* Redistribution and use in source and binary forms, with or without
5* modification, are permitted provided that the following conditions
6* are met:
7*
8* 1.  Redistributions of source code must retain the above copyright
9*     notice, this list of conditions and the following disclaimer.
10* 2.  Redistributions in binary form must reproduce the above copyright
11*     notice, this list of conditions and the following disclaimer in the
12*     documentation and/or other materials provided with the distribution.
13* 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14*     its contributors may be used to endorse or promote products derived
15*     from this software without specific prior written permission.
16*
17* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include "config.h"
30#include "core/accessibility/AccessibilityNodeObject.h"
31
32#include "core/accessibility/AXObjectCache.h"
33#include "core/dom/NodeTraversal.h"
34#include "core/dom/Text.h"
35#include "core/dom/UserGestureIndicator.h"
36#include "core/html/HTMLAnchorElement.h"
37#include "core/html/HTMLFrameElementBase.h"
38#include "core/html/HTMLInputElement.h"
39#include "core/html/HTMLLabelElement.h"
40#include "core/html/HTMLSelectElement.h"
41#include "core/html/HTMLTextAreaElement.h"
42#include "wtf/text/StringBuilder.h"
43
44using namespace std;
45
46namespace WebCore {
47
48using namespace HTMLNames;
49
50AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
51    : AccessibilityObject()
52    , m_ariaRole(UnknownRole)
53    , m_childrenDirty(false)
54#ifndef NDEBUG
55    , m_initialized(false)
56#endif
57    , m_node(node)
58{
59}
60
61PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
62{
63    return adoptRef(new AccessibilityNodeObject(node));
64}
65
66AccessibilityNodeObject::~AccessibilityNodeObject()
67{
68    ASSERT(isDetached());
69}
70
71// This function implements the ARIA accessible name as described by the Mozilla
72// ARIA Implementer's Guide.
73static String accessibleNameForNode(Node* node)
74{
75    if (node->isTextNode())
76        return toText(node)->data();
77
78    if (node->hasTagName(inputTag))
79        return toHTMLInputElement(node)->value();
80
81    if (node->isHTMLElement()) {
82        const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
83        if (!alt.isEmpty())
84            return alt;
85    }
86
87    return String();
88}
89
90String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
91{
92    StringBuilder builder;
93    unsigned size = elements.size();
94    for (unsigned i = 0; i < size; ++i) {
95        Element* idElement = elements[i];
96
97        builder.append(accessibleNameForNode(idElement));
98        for (Node* n = idElement->firstChild(); n; n = NodeTraversal::next(n, idElement))
99            builder.append(accessibleNameForNode(n));
100
101        if (i != size - 1)
102            builder.append(' ');
103    }
104    return builder.toString();
105}
106
107void AccessibilityNodeObject::alterSliderValue(bool increase)
108{
109    if (roleValue() != SliderRole)
110        return;
111
112    if (!getAttribute(stepAttr).isEmpty())
113        changeValueByStep(increase);
114    else
115        changeValueByPercent(increase ? 5 : -5);
116}
117
118String AccessibilityNodeObject::ariaAccessibilityDescription() const
119{
120    String ariaLabeledBy = ariaLabeledByAttribute();
121    if (!ariaLabeledBy.isEmpty())
122        return ariaLabeledBy;
123
124    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
125    if (!ariaLabel.isEmpty())
126        return ariaLabel;
127
128    return String();
129}
130
131
132void AccessibilityNodeObject::ariaLabeledByElements(Vector<Element*>& elements) const
133{
134    elementsFromAttribute(elements, aria_labeledbyAttr);
135    if (!elements.size())
136        elementsFromAttribute(elements, aria_labelledbyAttr);
137}
138
139void AccessibilityNodeObject::changeValueByStep(bool increase)
140{
141    float step = stepValueForRange();
142    float value = valueForRange();
143
144    value += increase ? step : -step;
145
146    setValue(String::number(value));
147
148    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
149}
150
151bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
152{
153#ifndef NDEBUG
154    // Double-check that an AccessibilityObject is never accessed before
155    // it's been initialized.
156    ASSERT(m_initialized);
157#endif
158
159    // If this element is within a parent that cannot have children, it should not be exposed.
160    if (isDescendantOfBarrenParent())
161        return true;
162
163    return m_role == UnknownRole;
164}
165
166AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
167{
168    if (!node())
169        return UnknownRole;
170
171    m_ariaRole = determineAriaRoleAttribute();
172
173    AccessibilityRole ariaRole = ariaRoleAttribute();
174    if (ariaRole != UnknownRole)
175        return ariaRole;
176
177    if (node()->isLink())
178        return WebCoreLinkRole;
179    if (node()->isTextNode())
180        return StaticTextRole;
181    if (node()->hasTagName(buttonTag))
182        return buttonRoleType();
183    if (node()->hasTagName(inputTag)) {
184        HTMLInputElement* input = toHTMLInputElement(node());
185        if (input->isCheckbox())
186            return CheckBoxRole;
187        if (input->isRadioButton())
188            return RadioButtonRole;
189        if (input->isTextButton())
190            return buttonRoleType();
191        if (input->isRangeControl())
192            return SliderRole;
193
194        const AtomicString& type = input->getAttribute(typeAttr);
195        if (equalIgnoringCase(type, "color"))
196            return ColorWellRole;
197
198        return TextFieldRole;
199    }
200    if (node()->hasTagName(selectTag)) {
201        HTMLSelectElement* selectElement = toHTMLSelectElement(node());
202        return selectElement->multiple() ? ListBoxRole : PopUpButtonRole;
203    }
204    if (isHTMLTextAreaElement(node()))
205        return TextAreaRole;
206    if (headingLevel())
207        return HeadingRole;
208    if (node()->hasTagName(divTag))
209        return DivRole;
210    if (node()->hasTagName(pTag))
211        return ParagraphRole;
212    if (isHTMLLabelElement(node()))
213        return LabelRole;
214    if (node()->isElementNode() && toElement(node())->isFocusable())
215        return GroupRole;
216
217    return UnknownRole;
218}
219
220AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
221{
222    const AtomicString& ariaRole = getAttribute(roleAttr);
223    if (ariaRole.isNull() || ariaRole.isEmpty())
224        return UnknownRole;
225
226    AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
227
228    // ARIA states if an item can get focus, it should not be presentational.
229    if (role == PresentationalRole && canSetFocusAttribute())
230        return UnknownRole;
231
232    if (role == ButtonRole)
233        role = buttonRoleType();
234
235    if (role == TextAreaRole && !ariaIsMultiline())
236        role = TextFieldRole;
237
238    role = remapAriaRoleDueToParent(role);
239
240    if (role)
241        return role;
242
243    return UnknownRole;
244}
245
246void AccessibilityNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
247{
248    Node* node = this->node();
249    if (!node || !node->isElementNode())
250        return;
251
252    TreeScope* scope = node->treeScope();
253    if (!scope)
254        return;
255
256    String idList = getAttribute(attribute).string();
257    if (idList.isEmpty())
258        return;
259
260    idList.replace('\n', ' ');
261    Vector<String> idVector;
262    idList.split(' ', idVector);
263
264    unsigned size = idVector.size();
265    for (unsigned i = 0; i < size; ++i) {
266        AtomicString idName(idVector[i]);
267        Element* idElement = scope->getElementById(idName);
268        if (idElement)
269            elements.append(idElement);
270    }
271}
272
273// If you call node->rendererIsEditable() since that will return true if an ancestor is editable.
274// This only returns true if this is the element that actually has the contentEditable attribute set.
275bool AccessibilityNodeObject::hasContentEditableAttributeSet() const
276{
277    if (!hasAttribute(contenteditableAttr))
278        return false;
279    const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
280    // Both "true" (case-insensitive) and the empty string count as true.
281    return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
282}
283
284bool AccessibilityNodeObject::isARIARange() const
285{
286    switch (m_ariaRole) {
287    case ProgressIndicatorRole:
288    case SliderRole:
289    case ScrollBarRole:
290    case SpinButtonRole:
291        return true;
292    default:
293        return false;
294    }
295}
296
297bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
298{
299    for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
300        if (!object->canHaveChildren())
301            return true;
302    }
303
304    return false;
305}
306
307bool AccessibilityNodeObject::isGenericFocusableElement() const
308{
309    if (!canSetFocusAttribute())
310        return false;
311
312     // If it's a control, it's not generic.
313     if (isControl())
314        return false;
315
316    // If it has an aria role, it's not generic.
317    if (m_ariaRole != UnknownRole)
318        return false;
319
320    // If the content editable attribute is set on this element, that's the reason
321    // it's focusable, and existing logic should handle this case already - so it's not a
322    // generic focusable element.
323
324    if (hasContentEditableAttributeSet())
325        return false;
326
327    // The web area and body element are both focusable, but existing logic handles these
328    // cases already, so we don't need to include them here.
329    if (roleValue() == WebAreaRole)
330        return false;
331    if (node() && node()->hasTagName(bodyTag))
332        return false;
333
334    // An SVG root is focusable by default, but it's probably not interactive, so don't
335    // include it. It can still be made accessible by giving it an ARIA role.
336    if (roleValue() == SVGRootRole)
337        return false;
338
339    return true;
340}
341
342HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const
343{
344    if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
345        return 0;
346
347    const AtomicString& id = element->getIdAttribute();
348    if (!id.isEmpty()) {
349        if (HTMLLabelElement* label = element->treeScope()->labelElementForId(id))
350            return label;
351    }
352
353    for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) {
354        if (isHTMLLabelElement(parent))
355            return toHTMLLabelElement(parent);
356    }
357
358    return 0;
359}
360
361AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const
362{
363    Element* menuItem = menuItemElementForMenu();
364
365    if (menuItem) {
366        // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
367        AccessibilityObject* menuItemAX = axObjectCache()->getOrCreate(menuItem);
368        if (menuItemAX && menuItemAX->isMenuButton())
369            return menuItemAX;
370    }
371    return 0;
372}
373
374static Element* siblingWithAriaRole(String role, Node* node)
375{
376    Node* parent = node->parentNode();
377    if (!parent)
378        return 0;
379
380    for (Node* sibling = parent->firstChild(); sibling; sibling = sibling->nextSibling()) {
381        if (sibling->isElementNode()) {
382            const AtomicString& siblingAriaRole = toElement(sibling)->getAttribute(roleAttr);
383            if (equalIgnoringCase(siblingAriaRole, role))
384                return toElement(sibling);
385        }
386    }
387
388    return 0;
389}
390
391Element* AccessibilityNodeObject::menuItemElementForMenu() const
392{
393    if (ariaRoleAttribute() != MenuRole)
394        return 0;
395
396    return siblingWithAriaRole("menuitem", node());
397}
398
399Element* AccessibilityNodeObject::mouseButtonListener() const
400{
401    Node* node = this->node();
402    if (!node)
403        return 0;
404
405    // check if our parent is a mouse button listener
406    while (node && !node->isElementNode())
407        node = node->parentNode();
408
409    if (!node)
410        return 0;
411
412    // FIXME: Do the continuation search like anchorElement does
413    for (Element* element = toElement(node); element; element = element->parentElement()) {
414        if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
415            return element;
416    }
417
418    return 0;
419}
420
421AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
422{
423    // Some objects change their role based on their parent.
424    // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop.
425    // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
426    // https://bugs.webkit.org/show_bug.cgi?id=65174
427
428    if (role != ListBoxOptionRole && role != MenuItemRole)
429        return role;
430
431    for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
432        AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
433
434        // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
435        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
436            return MenuItemRole;
437        // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
438        if (role == MenuItemRole && parentAriaRole == GroupRole)
439            return MenuButtonRole;
440
441        // If the parent had a different role, then we don't need to continue searching up the chain.
442        if (parentAriaRole)
443            break;
444    }
445
446    return role;
447}
448
449void AccessibilityNodeObject::init()
450{
451#ifndef NDEBUG
452    ASSERT(!m_initialized);
453    m_initialized = true;
454#endif
455    m_role = determineAccessibilityRole();
456}
457
458void AccessibilityNodeObject::detach()
459{
460    clearChildren();
461    AccessibilityObject::detach();
462    m_node = 0;
463}
464
465bool AccessibilityNodeObject::isAnchor() const
466{
467    return !isNativeImage() && isLink();
468}
469
470bool AccessibilityNodeObject::isControl() const
471{
472    Node* node = this->node();
473    if (!node)
474        return false;
475
476    return ((node->isElementNode() && toElement(node)->isFormControlElement())
477        || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
478}
479
480bool AccessibilityNodeObject::isFieldset() const
481{
482    Node* node = this->node();
483    if (!node)
484        return false;
485
486    return node->hasTagName(fieldsetTag);
487}
488
489bool AccessibilityNodeObject::isHeading() const
490{
491    return roleValue() == HeadingRole;
492}
493
494bool AccessibilityNodeObject::isHovered() const
495{
496    Node* node = this->node();
497    if (!node)
498        return false;
499
500    return node->hovered();
501}
502
503bool AccessibilityNodeObject::isImage() const
504{
505    return roleValue() == ImageRole;
506}
507
508bool AccessibilityNodeObject::isImageButton() const
509{
510    return isNativeImage() && isButton();
511}
512
513bool AccessibilityNodeObject::isInputImage() const
514{
515    Node* node = this->node();
516    if (!node)
517        return false;
518
519    if (roleValue() == ButtonRole && node->hasTagName(inputTag))
520        return toHTMLInputElement(node)->isImageButton();
521
522    return false;
523}
524
525bool AccessibilityNodeObject::isLink() const
526{
527    return roleValue() == WebCoreLinkRole;
528}
529
530bool AccessibilityNodeObject::isMenu() const
531{
532    return roleValue() == MenuRole;
533}
534
535bool AccessibilityNodeObject::isMenuButton() const
536{
537    return roleValue() == MenuButtonRole;
538}
539
540bool AccessibilityNodeObject::isMultiSelectable() const
541{
542    const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
543    if (equalIgnoringCase(ariaMultiSelectable, "true"))
544        return true;
545    if (equalIgnoringCase(ariaMultiSelectable, "false"))
546        return false;
547
548    return node() && node()->hasTagName(selectTag) && toHTMLSelectElement(node())->multiple();
549}
550
551bool AccessibilityNodeObject::isNativeCheckboxOrRadio() const
552{
553    Node* node = this->node();
554    if (!node || !node->hasTagName(inputTag))
555        return false;
556
557    HTMLInputElement* input = toHTMLInputElement(node);
558    return input->isCheckbox() || input->isRadioButton();
559}
560
561bool AccessibilityNodeObject::isNativeImage() const
562{
563    Node* node = this->node();
564    if (!node)
565        return false;
566
567    if (node->hasTagName(imgTag))
568        return true;
569
570    if (node->hasTagName(appletTag) || node->hasTagName(embedTag) || node->hasTagName(objectTag))
571        return true;
572
573    if (node->hasTagName(inputTag))
574        return toHTMLInputElement(node)->isImageButton();
575
576    return false;
577}
578
579bool AccessibilityNodeObject::isNativeTextControl() const
580{
581    Node* node = this->node();
582    if (!node)
583        return false;
584
585    if (isHTMLTextAreaElement(node))
586        return true;
587
588    if (node->hasTagName(inputTag)) {
589        HTMLInputElement* input = toHTMLInputElement(node);
590        return input->isText() || input->isNumberField();
591    }
592
593    return false;
594}
595
596bool AccessibilityNodeObject::isNonNativeTextControl() const
597{
598    if (isNativeTextControl())
599        return false;
600
601    if (hasContentEditableAttributeSet())
602        return true;
603
604    if (isARIATextControl())
605        return true;
606
607    return false;
608}
609
610bool AccessibilityNodeObject::isPasswordField() const
611{
612    Node* node = this->node();
613    if (!node || !node->hasTagName(inputTag))
614        return false;
615
616    if (ariaRoleAttribute() != UnknownRole)
617        return false;
618
619    return toHTMLInputElement(node)->isPasswordField();
620}
621
622bool AccessibilityNodeObject::isProgressIndicator() const
623{
624    return roleValue() == ProgressIndicatorRole;
625}
626
627bool AccessibilityNodeObject::isSlider() const
628{
629    return roleValue() == SliderRole;
630}
631
632bool AccessibilityNodeObject::isChecked() const
633{
634    Node* node = this->node();
635    if (!node)
636        return false;
637
638    // First test for native checkedness semantics
639    if (node->hasTagName(inputTag))
640        return toHTMLInputElement(node)->shouldAppearChecked();
641
642    // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
643    AccessibilityRole ariaRole = ariaRoleAttribute();
644    if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
645        if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
646            return true;
647        return false;
648    }
649
650    // Otherwise it's not checked
651    return false;
652}
653
654bool AccessibilityNodeObject::isEnabled() const
655{
656    if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
657        return false;
658
659    Node* node = this->node();
660    if (!node || !node->isElementNode())
661        return true;
662
663    return !toElement(node)->isDisabledFormControl();
664}
665
666bool AccessibilityNodeObject::isIndeterminate() const
667{
668    Node* node = this->node();
669    if (!node || !node->hasTagName(inputTag))
670        return false;
671
672    return toHTMLInputElement(node)->shouldAppearIndeterminate();
673}
674
675bool AccessibilityNodeObject::isPressed() const
676{
677    if (!isButton())
678        return false;
679
680    Node* node = this->node();
681    if (!node)
682        return false;
683
684    // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
685    if (ariaRoleAttribute() == ButtonRole) {
686        if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
687            return true;
688        return false;
689    }
690
691    return node->active();
692}
693
694bool AccessibilityNodeObject::isReadOnly() const
695{
696    Node* node = this->node();
697    if (!node)
698        return true;
699
700    if (isHTMLTextAreaElement(node))
701        return toHTMLFormControlElement(node)->isReadOnly();
702
703    if (node->hasTagName(inputTag)) {
704        HTMLInputElement* input = toHTMLInputElement(node);
705        if (input->isTextField())
706            return input->isReadOnly();
707    }
708
709    return !node->rendererIsEditable();
710}
711
712bool AccessibilityNodeObject::isRequired() const
713{
714    if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
715        return true;
716
717    Node* n = this->node();
718    if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
719        return toHTMLFormControlElement(n)->isRequired();
720
721    return false;
722}
723
724bool AccessibilityNodeObject::canSetFocusAttribute() const
725{
726    Node* node = this->node();
727    if (!node)
728        return false;
729
730    if (isWebArea())
731        return true;
732
733    // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
734    // do anything. For example, setFocusedNode() will do nothing if the current focused
735    // node will not relinquish the focus.
736    if (!node)
737        return false;
738
739    if (isDisabledFormControl(node))
740        return false;
741
742    return node->isElementNode() && toElement(node)->supportsFocus();
743}
744
745bool AccessibilityNodeObject::canvasHasFallbackContent() const
746{
747    Node* node = this->node();
748    if (!node || !node->hasTagName(canvasTag))
749        return false;
750
751    // If it has any children that are elements, we'll assume it might be fallback
752    // content. If it has no children or its only children are not elements
753    // (e.g. just text nodes), it doesn't have fallback content.
754    for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
755        if (child->isElementNode())
756            return true;
757    }
758
759    return false;
760}
761
762int AccessibilityNodeObject::headingLevel() const
763{
764    // headings can be in block flow and non-block flow
765    Node* node = this->node();
766    if (!node)
767        return false;
768
769    if (ariaRoleAttribute() == HeadingRole)
770        return getAttribute(aria_levelAttr).toInt();
771
772    if (node->hasTagName(h1Tag))
773        return 1;
774
775    if (node->hasTagName(h2Tag))
776        return 2;
777
778    if (node->hasTagName(h3Tag))
779        return 3;
780
781    if (node->hasTagName(h4Tag))
782        return 4;
783
784    if (node->hasTagName(h5Tag))
785        return 5;
786
787    if (node->hasTagName(h6Tag))
788        return 6;
789
790    return 0;
791}
792
793unsigned AccessibilityNodeObject::hierarchicalLevel() const
794{
795    Node* node = this->node();
796    if (!node || !node->isElementNode())
797        return 0;
798    Element* element = toElement(node);
799    String ariaLevel = element->getAttribute(aria_levelAttr);
800    if (!ariaLevel.isEmpty())
801        return ariaLevel.toInt();
802
803    // Only tree item will calculate its level through the DOM currently.
804    if (roleValue() != TreeItemRole)
805        return 0;
806
807    // Hierarchy leveling starts at 1, to match the aria-level spec.
808    // We measure tree hierarchy by the number of groups that the item is within.
809    unsigned level = 1;
810    for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
811        AccessibilityRole parentRole = parent->roleValue();
812        if (parentRole == GroupRole)
813            level++;
814        else if (parentRole == TreeRole)
815            break;
816    }
817
818    return level;
819}
820
821String AccessibilityNodeObject::text() const
822{
823    // If this is a user defined static text, use the accessible name computation.
824    if (ariaRoleAttribute() == StaticTextRole)
825        return ariaAccessibilityDescription();
826
827    if (!isTextControl())
828        return String();
829
830    Node* node = this->node();
831    if (!node)
832        return String();
833
834    if (isNativeTextControl() && (isHTMLTextAreaElement(node) || node->hasTagName(inputTag)))
835        return toHTMLTextFormControlElement(node)->value();
836
837    if (!node->isElementNode())
838        return String();
839
840    return toElement(node)->innerText();
841}
842
843AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
844{
845    if (isNativeCheckboxOrRadio())
846        return isChecked() ? ButtonStateOn : ButtonStateOff;
847
848    return AccessibilityObject::checkboxOrRadioValue();
849}
850
851void AccessibilityNodeObject::colorValue(int& r, int& g, int& b) const
852{
853    r = 0;
854    g = 0;
855    b = 0;
856
857    if (!isColorWell())
858        return;
859
860    if (!node() || !node()->hasTagName(inputTag))
861        return;
862
863    HTMLInputElement* input = toHTMLInputElement(node());
864    const AtomicString& type = input->getAttribute(typeAttr);
865    if (!equalIgnoringCase(type, "color"))
866        return;
867
868    // HTMLInputElement::value always returns a string parseable by Color().
869    StyleColor color(input->value());
870    r = color.red();
871    g = color.green();
872    b = color.blue();
873}
874
875String AccessibilityNodeObject::valueDescription() const
876{
877    if (!isARIARange())
878        return String();
879
880    return getAttribute(aria_valuetextAttr).string();
881}
882
883float AccessibilityNodeObject::valueForRange() const
884{
885    if (node() && node()->hasTagName(inputTag)) {
886        HTMLInputElement* input = toHTMLInputElement(node());
887        if (input->isRangeControl())
888            return input->valueAsNumber();
889    }
890
891    if (!isARIARange())
892        return 0.0f;
893
894    return getAttribute(aria_valuenowAttr).toFloat();
895}
896
897float AccessibilityNodeObject::maxValueForRange() const
898{
899    if (node() && node()->hasTagName(inputTag)) {
900        HTMLInputElement* input = toHTMLInputElement(node());
901        if (input->isRangeControl())
902            return input->maximum();
903    }
904
905    if (!isARIARange())
906        return 0.0f;
907
908    return getAttribute(aria_valuemaxAttr).toFloat();
909}
910
911float AccessibilityNodeObject::minValueForRange() const
912{
913    if (node() && node()->hasTagName(inputTag)) {
914        HTMLInputElement* input = toHTMLInputElement(node());
915        if (input->isRangeControl())
916            return input->minimum();
917    }
918
919    if (!isARIARange())
920        return 0.0f;
921
922    return getAttribute(aria_valueminAttr).toFloat();
923}
924
925float AccessibilityNodeObject::stepValueForRange() const
926{
927    return getAttribute(stepAttr).toFloat();
928}
929
930String AccessibilityNodeObject::stringValue() const
931{
932    Node* node = this->node();
933    if (!node)
934        return String();
935
936    if (ariaRoleAttribute() == StaticTextRole) {
937        String staticText = text();
938        if (!staticText.length())
939            staticText = textUnderElement();
940        return staticText;
941    }
942
943    if (node->isTextNode())
944        return textUnderElement();
945
946    if (node->hasTagName(selectTag)) {
947        HTMLSelectElement* selectElement = toHTMLSelectElement(node);
948        int selectedIndex = selectElement->selectedIndex();
949        const Vector<HTMLElement*> listItems = selectElement->listItems();
950        if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
951            const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
952            if (!overriddenDescription.isNull())
953                return overriddenDescription;
954        }
955        if (!selectElement->multiple())
956            return selectElement->value();
957        return String();
958    }
959
960    if (isTextControl())
961        return text();
962
963    // FIXME: We might need to implement a value here for more types
964    // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
965    // this would require subclassing or making accessibilityAttributeNames do something other than return a
966    // single static array.
967    return String();
968}
969
970String AccessibilityNodeObject::ariaDescribedByAttribute() const
971{
972    Vector<Element*> elements;
973    elementsFromAttribute(elements, aria_describedbyAttr);
974
975    return accessibilityDescriptionForElements(elements);
976}
977
978
979String AccessibilityNodeObject::ariaLabeledByAttribute() const
980{
981    Vector<Element*> elements;
982    ariaLabeledByElements(elements);
983
984    return accessibilityDescriptionForElements(elements);
985}
986
987AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
988{
989    return m_ariaRole;
990}
991
992void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textOrder)
993{
994    titleElementText(textOrder);
995    alternativeText(textOrder);
996    visibleText(textOrder);
997    helpText(textOrder);
998
999    String placeholder = placeholderValue();
1000    if (!placeholder.isEmpty())
1001        textOrder.append(AccessibilityText(placeholder, PlaceholderText));
1002}
1003
1004// When building the textUnderElement for an object, determine whether or not
1005// we should include the inner text of this given descendant object or skip it.
1006static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj)
1007{
1008    // Consider this hypothetical example:
1009    // <div tabindex=0>
1010    //   <h2>
1011    //     Table of contents
1012    //   </h2>
1013    //   <a href="#start">Jump to start of book</a>
1014    //   <ul>
1015    //     <li><a href="#1">Chapter 1</a></li>
1016    //     <li><a href="#1">Chapter 2</a></li>
1017    //   </ul>
1018    // </div>
1019    //
1020    // The goal is to return a reasonable title for the outer container div, because
1021    // it's focusable - but without making its title be the full inner text, which is
1022    // quite long. As a heuristic, skip links, controls, and elements that are usually
1023    // containers with lots of children.
1024
1025    // Skip focusable children, so we don't include the text of links and controls.
1026    if (obj->canSetFocusAttribute())
1027        return false;
1028
1029    // Skip big container elements like lists, tables, etc.
1030    if (obj->isList() || obj->isAccessibilityTable() || obj->isTree() || obj->isCanvas())
1031        return false;
1032
1033    return true;
1034}
1035
1036String AccessibilityNodeObject::textUnderElement() const
1037{
1038    Node* node = this->node();
1039    if (node && node->isTextNode())
1040        return toText(node)->wholeText();
1041
1042    StringBuilder builder;
1043    for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
1044        if (!shouldUseAccessiblityObjectInnerText(child))
1045            continue;
1046
1047        if (child->isAccessibilityNodeObject()) {
1048            Vector<AccessibilityText> textOrder;
1049            toAccessibilityNodeObject(child)->alternativeText(textOrder);
1050            if (textOrder.size() > 0) {
1051                builder.append(textOrder[0].text);
1052                continue;
1053            }
1054        }
1055
1056        builder.append(child->textUnderElement());
1057    }
1058
1059    return builder.toString();
1060}
1061
1062String AccessibilityNodeObject::accessibilityDescription() const
1063{
1064    // Static text should not have a description, it should only have a stringValue.
1065    if (roleValue() == StaticTextRole)
1066        return String();
1067
1068    String ariaDescription = ariaAccessibilityDescription();
1069    if (!ariaDescription.isEmpty())
1070        return ariaDescription;
1071
1072    if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
1073        // Images should use alt as long as the attribute is present, even if empty.
1074        // Otherwise, it should fallback to other methods, like the title attribute.
1075        const AtomicString& alt = getAttribute(altAttr);
1076        if (!alt.isNull())
1077            return alt;
1078    }
1079
1080    // An element's descriptive text is comprised of title() (what's visible on the screen) and accessibilityDescription() (other descriptive text).
1081    // Both are used to generate what a screen reader speaks.
1082    // If this point is reached (i.e. there's no accessibilityDescription) and there's no title(), we should fallback to using the title attribute.
1083    // 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).
1084    if (title().isEmpty())
1085        return getAttribute(titleAttr);
1086
1087    return String();
1088}
1089
1090String AccessibilityNodeObject::title() const
1091{
1092    Node* node = this->node();
1093    if (!node)
1094        return String();
1095
1096    bool isInputTag = node->hasTagName(inputTag);
1097    if (isInputTag) {
1098        HTMLInputElement* input = toHTMLInputElement(node);
1099        if (input->isTextButton())
1100            return input->valueWithDefault();
1101    }
1102
1103    if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1104        HTMLLabelElement* label = labelForElement(toElement(node));
1105        if (label && !exposesTitleUIElement())
1106            return label->innerText();
1107    }
1108
1109    // If this node isn't rendered, there's no inner text we can extract from a select element.
1110    if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1111        return String();
1112
1113    switch (roleValue()) {
1114    case PopUpButtonRole:
1115        // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1116        if (node->hasTagName(selectTag))
1117            return String();
1118    case ButtonRole:
1119    case ToggleButtonRole:
1120    case CheckBoxRole:
1121    case ListBoxOptionRole:
1122    case MenuButtonRole:
1123    case MenuItemRole:
1124    case RadioButtonRole:
1125    case TabRole:
1126        return textUnderElement();
1127    // SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
1128    case SVGRootRole:
1129        return String();
1130    default:
1131        break;
1132    }
1133
1134    if (isHeading() || isLink())
1135        return textUnderElement();
1136
1137    // If it's focusable but it's not content editable or a known control type, then it will appear to
1138    // the user as a single atomic object, so we should use its text as the default title.
1139    if (isGenericFocusableElement())
1140        return textUnderElement();
1141
1142    return String();
1143}
1144
1145String AccessibilityNodeObject::helpText() const
1146{
1147    Node* node = this->node();
1148    if (!node)
1149        return String();
1150
1151    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1152    if (!ariaHelp.isEmpty())
1153        return ariaHelp;
1154
1155    String describedBy = ariaDescribedByAttribute();
1156    if (!describedBy.isEmpty())
1157        return describedBy;
1158
1159    String description = accessibilityDescription();
1160    for (Node* curr = node; curr; curr = curr->parentNode()) {
1161        if (curr->isHTMLElement()) {
1162            const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
1163            if (!summary.isEmpty())
1164                return summary;
1165
1166            // The title attribute should be used as help text unless it is already being used as descriptive text.
1167            const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
1168            if (!title.isEmpty() && description != title)
1169                return title;
1170        }
1171
1172        // Only take help text from an ancestor element if its a group or an unknown role. If help was
1173        // added to those kinds of elements, it is likely it was meant for a child element.
1174        AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
1175        if (axObj) {
1176            AccessibilityRole role = axObj->roleValue();
1177            if (role != GroupRole && role != UnknownRole)
1178                break;
1179        }
1180    }
1181
1182    return String();
1183}
1184
1185LayoutRect AccessibilityNodeObject::elementRect() const
1186{
1187    // First check if it has a custom rect, for example if this element is tied to a canvas path.
1188    if (!m_explicitElementRect.isEmpty())
1189        return m_explicitElementRect;
1190
1191    // AccessibilityNodeObjects have no mechanism yet to return a size or position.
1192    // For now, let's return the position of the ancestor that does have a position,
1193    // 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.
1194
1195    LayoutRect boundingBox;
1196
1197    for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
1198        if (positionProvider->isAccessibilityRenderObject()) {
1199            LayoutRect parentRect = positionProvider->elementRect();
1200            boundingBox.setSize(LayoutSize(parentRect.width(), LayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
1201            boundingBox.setLocation(parentRect.location());
1202            break;
1203        }
1204    }
1205
1206    return boundingBox;
1207}
1208
1209AccessibilityObject* AccessibilityNodeObject::parentObject() const
1210{
1211    if (!node())
1212        return 0;
1213
1214    Node* parentObj = node()->parentNode();
1215    if (parentObj)
1216        return axObjectCache()->getOrCreate(parentObj);
1217
1218    return 0;
1219}
1220
1221AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
1222{
1223    return parentObject();
1224}
1225
1226AccessibilityObject* AccessibilityNodeObject::firstChild() const
1227{
1228    if (!node())
1229        return 0;
1230
1231    Node* firstChild = node()->firstChild();
1232
1233    if (!firstChild)
1234        return 0;
1235
1236    return axObjectCache()->getOrCreate(firstChild);
1237}
1238
1239AccessibilityObject* AccessibilityNodeObject::nextSibling() const
1240{
1241    if (!node())
1242        return 0;
1243
1244    Node* nextSibling = node()->nextSibling();
1245    if (!nextSibling)
1246        return 0;
1247
1248    return axObjectCache()->getOrCreate(nextSibling);
1249}
1250
1251void AccessibilityNodeObject::addChildren()
1252{
1253    // If the need to add more children in addition to existing children arises,
1254    // childrenChanged should have been called, leaving the object with no children.
1255    ASSERT(!m_haveChildren);
1256
1257    if (!m_node)
1258        return;
1259
1260    m_haveChildren = true;
1261
1262    // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
1263    if (renderer() && !m_node->hasTagName(canvasTag))
1264        return;
1265
1266    for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
1267        addChild(axObjectCache()->getOrCreate(child));
1268}
1269
1270void AccessibilityNodeObject::addChild(AccessibilityObject* child)
1271{
1272    insertChild(child, m_children.size());
1273}
1274
1275void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index)
1276{
1277    if (!child)
1278        return;
1279
1280    // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
1281    // or its visibility has changed. In the latter case, this child may have a stale child cached.
1282    // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
1283    child->clearChildren();
1284
1285    if (child->accessibilityIsIgnored()) {
1286        AccessibilityChildrenVector children = child->children();
1287        size_t length = children.size();
1288        for (size_t i = 0; i < length; ++i)
1289            m_children.insert(index + i, children[i]);
1290    } else {
1291        ASSERT(child->parentObject() == this);
1292        m_children.insert(index, child);
1293    }
1294}
1295
1296bool AccessibilityNodeObject::canHaveChildren() const
1297{
1298    // If this is an AccessibilityRenderObject, then it's okay if this object
1299    // doesn't have a node - there are some renderers that don't have associated
1300    // nodes, like scroll areas and css-generated text.
1301    if (!node() && !isAccessibilityRenderObject())
1302        return false;
1303
1304    // Elements that should not have children
1305    switch (roleValue()) {
1306    case ImageRole:
1307    case ButtonRole:
1308    case PopUpButtonRole:
1309    case CheckBoxRole:
1310    case RadioButtonRole:
1311    case TabRole:
1312    case ToggleButtonRole:
1313    case StaticTextRole:
1314    case ListBoxOptionRole:
1315    case ScrollBarRole:
1316        return false;
1317    default:
1318        return true;
1319    }
1320}
1321
1322Element* AccessibilityNodeObject::actionElement() const
1323{
1324    Node* node = this->node();
1325    if (!node)
1326        return 0;
1327
1328    if (node->hasTagName(inputTag)) {
1329        HTMLInputElement* input = toHTMLInputElement(node);
1330        if (!input->isDisabledFormControl() && (isCheckboxOrRadio() || input->isTextButton()))
1331            return input;
1332    } else if (node->hasTagName(buttonTag))
1333        return toElement(node);
1334
1335    if (isFileUploadButton())
1336        return toElement(node);
1337
1338    if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
1339        return toElement(node);
1340
1341    if (isImageButton())
1342        return toElement(node);
1343
1344    if (node->hasTagName(selectTag))
1345        return toElement(node);
1346
1347    switch (roleValue()) {
1348    case ButtonRole:
1349    case PopUpButtonRole:
1350    case ToggleButtonRole:
1351    case TabRole:
1352    case MenuItemRole:
1353    case ListItemRole:
1354        return toElement(node);
1355    default:
1356        break;
1357    }
1358
1359    Element* elt = anchorElement();
1360    if (!elt)
1361        elt = mouseButtonListener();
1362    return elt;
1363}
1364
1365Element* AccessibilityNodeObject::anchorElement() const
1366{
1367    Node* node = this->node();
1368    if (!node)
1369        return 0;
1370
1371    AXObjectCache* cache = axObjectCache();
1372
1373    // search up the DOM tree for an anchor element
1374    // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
1375    for ( ; node; node = node->parentNode()) {
1376        if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
1377            return toElement(node);
1378    }
1379
1380    return 0;
1381}
1382
1383Document* AccessibilityNodeObject::document() const
1384{
1385    if (!node())
1386        return 0;
1387    return node()->document();
1388}
1389
1390void AccessibilityNodeObject::setNode(Node* node)
1391{
1392    m_node = node;
1393}
1394
1395void AccessibilityNodeObject::increment()
1396{
1397    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
1398    alterSliderValue(true);
1399}
1400
1401void AccessibilityNodeObject::decrement()
1402{
1403    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
1404    alterSliderValue(false);
1405}
1406
1407void AccessibilityNodeObject::childrenChanged()
1408{
1409    // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
1410    if (!node() && !renderer())
1411        return;
1412
1413    axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
1414
1415    // Go up the accessibility parent chain, but only if the element already exists. This method is
1416    // called during render layouts, minimal work should be done.
1417    // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
1418    // At the same time, process ARIA live region changes.
1419    for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
1420        parent->setNeedsToUpdateChildren();
1421
1422        // These notifications always need to be sent because screenreaders are reliant on them to perform.
1423        // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
1424
1425        // If this element supports ARIA live regions, then notify the AT of changes.
1426        if (parent->supportsARIALiveRegion())
1427            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
1428
1429        // If this element is an ARIA text box or content editable, post a "value changed" notification on it
1430        // so that it behaves just like a native input element or textarea.
1431        if (isNonNativeTextControl())
1432            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
1433    }
1434}
1435
1436void AccessibilityNodeObject::selectionChanged()
1437{
1438    // When the selection changes, post the notification on the first ancestor that's an
1439    // ARIA text box, or that's marked as contentEditable, otherwise post the notification
1440    // on the web area.
1441    if (isNonNativeTextControl() || isWebArea())
1442        axObjectCache()->postNotification(this, document(), AXObjectCache::AXSelectedTextChanged, true);
1443    else
1444        AccessibilityObject::selectionChanged(); // Calls selectionChanged on parent.
1445}
1446
1447void AccessibilityNodeObject::textChanged()
1448{
1449    // If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
1450    // then notify the AT of changes.
1451    AXObjectCache* cache = axObjectCache();
1452    for (Node* parentNode = node(); parentNode; parentNode = parentNode->parentNode()) {
1453        AccessibilityObject* parent = cache->get(parentNode);
1454        if (!parent)
1455            continue;
1456
1457        if (parent->supportsARIALiveRegion())
1458            cache->postNotification(parentNode, AXObjectCache::AXLiveRegionChanged, true);
1459
1460        // If this element is an ARIA text box or content editable, post a "value changed" notification on it
1461        // so that it behaves just like a native input element or textarea.
1462        if (parent->isNonNativeTextControl())
1463            cache->postNotification(parentNode, AXObjectCache::AXValueChanged, true);
1464    }
1465}
1466
1467void AccessibilityNodeObject::updateAccessibilityRole()
1468{
1469    bool ignoredStatus = accessibilityIsIgnored();
1470    m_role = determineAccessibilityRole();
1471
1472    // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
1473    if (ignoredStatus != accessibilityIsIgnored())
1474        childrenChanged();
1475}
1476
1477String AccessibilityNodeObject::alternativeTextForWebArea() const
1478{
1479    // The WebArea description should follow this order:
1480    //     aria-label on the <html>
1481    //     title on the <html>
1482    //     <title> inside the <head> (of it was set through JS)
1483    //     name on the <html>
1484    // For iframes:
1485    //     aria-label on the <iframe>
1486    //     title on the <iframe>
1487    //     name on the <iframe>
1488
1489    Document* document = this->document();
1490    if (!document)
1491        return String();
1492
1493    // Check if the HTML element has an aria-label for the webpage.
1494    if (Element* documentElement = document->documentElement()) {
1495        const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
1496        if (!ariaLabel.isEmpty())
1497            return ariaLabel;
1498    }
1499
1500    Node* owner = document->ownerElement();
1501    if (owner) {
1502        if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1503            const AtomicString& title = toElement(owner)->getAttribute(titleAttr);
1504            if (!title.isEmpty())
1505                return title;
1506            return toElement(owner)->getNameAttribute();
1507        }
1508        if (owner->isHTMLElement())
1509            return toHTMLElement(owner)->getNameAttribute();
1510    }
1511
1512    String documentTitle = document->title();
1513    if (!documentTitle.isEmpty())
1514        return documentTitle;
1515
1516    owner = document->body();
1517    if (owner && owner->isHTMLElement())
1518        return toHTMLElement(owner)->getNameAttribute();
1519
1520    return String();
1521}
1522
1523void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
1524{
1525    if (isWebArea()) {
1526        String webAreaText = alternativeTextForWebArea();
1527        if (!webAreaText.isEmpty())
1528            textOrder.append(AccessibilityText(webAreaText, AlternativeText));
1529        return;
1530    }
1531
1532    ariaLabeledByText(textOrder);
1533
1534    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1535    if (!ariaLabel.isEmpty())
1536        textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
1537
1538    if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
1539        // Images should use alt as long as the attribute is present, even if empty.
1540        // Otherwise, it should fallback to other methods, like the title attribute.
1541        const AtomicString& alt = getAttribute(altAttr);
1542        if (!alt.isNull())
1543            textOrder.append(AccessibilityText(alt, AlternativeText));
1544    }
1545}
1546
1547void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
1548{
1549    String ariaLabeledBy = ariaLabeledByAttribute();
1550    if (!ariaLabeledBy.isEmpty()) {
1551        Vector<Element*> elements;
1552        ariaLabeledByElements(elements);
1553
1554        unsigned length = elements.size();
1555        for (unsigned k = 0; k < length; k++) {
1556            RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(elements[k]);
1557            textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElement));
1558        }
1559    }
1560}
1561
1562void AccessibilityNodeObject::changeValueByPercent(float percentChange)
1563{
1564    float range = maxValueForRange() - minValueForRange();
1565    float value = valueForRange();
1566
1567    value += range * (percentChange / 100);
1568    setValue(String::number(value));
1569
1570    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged, true);
1571}
1572
1573void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) const
1574{
1575    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1576    if (!ariaHelp.isEmpty())
1577        textOrder.append(AccessibilityText(ariaHelp, HelpText));
1578
1579    String describedBy = ariaDescribedByAttribute();
1580    if (!describedBy.isEmpty())
1581        textOrder.append(AccessibilityText(describedBy, SummaryText));
1582
1583    // Add help type text that is derived from ancestors.
1584    for (Node* curr = node(); curr; curr = curr->parentNode()) {
1585        const AtomicString& summary = getAttribute(summaryAttr);
1586        if (!summary.isEmpty())
1587            textOrder.append(AccessibilityText(summary, SummaryText));
1588
1589        // The title attribute should be used as help text unless it is already being used as descriptive text.
1590        const AtomicString& title = getAttribute(titleAttr);
1591        if (!title.isEmpty())
1592            textOrder.append(AccessibilityText(title, TitleTagText));
1593
1594        // Only take help text from an ancestor element if its a group or an unknown role. If help was
1595        // added to those kinds of elements, it is likely it was meant for a child element.
1596        AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
1597        if (!axObj)
1598            return;
1599
1600        AccessibilityRole role = axObj->roleValue();
1601        if (role != GroupRole && role != UnknownRole)
1602            break;
1603    }
1604}
1605
1606void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder)
1607{
1608    Node* node = this->node();
1609    if (!node)
1610        return;
1611
1612    bool isInputTag = node->hasTagName(inputTag);
1613    if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1614        HTMLLabelElement* label = labelForElement(toElement(node));
1615        if (label) {
1616            AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
1617            textOrder.append(AccessibilityText(label->innerText(), LabelByElementText, labelObject));
1618            return;
1619        }
1620    }
1621
1622    AccessibilityObject* titleUIElement = this->titleUIElement();
1623    if (titleUIElement)
1624        textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement));
1625}
1626
1627void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder) const
1628{
1629    Node* node = this->node();
1630    if (!node)
1631        return;
1632
1633    bool isInputTag = node->hasTagName(inputTag);
1634    if (isInputTag) {
1635        HTMLInputElement* input = toHTMLInputElement(node);
1636        if (input->isTextButton()) {
1637            textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText));
1638            return;
1639        }
1640    }
1641
1642    // If this node isn't rendered, there's no inner text we can extract from a select element.
1643    if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1644        return;
1645
1646    bool useTextUnderElement = false;
1647
1648    switch (roleValue()) {
1649    case PopUpButtonRole:
1650        // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1651        if (node->hasTagName(selectTag))
1652            break;
1653    case ButtonRole:
1654    case ToggleButtonRole:
1655    case CheckBoxRole:
1656    case ListBoxOptionRole:
1657    case MenuButtonRole:
1658    case MenuItemRole:
1659    case RadioButtonRole:
1660    case TabRole:
1661        useTextUnderElement = true;
1662        break;
1663    default:
1664        break;
1665    }
1666
1667    // If it's focusable but it's not content editable or a known control type, then it will appear to
1668    // the user as a single atomic object, so we should use its text as the default title.
1669    if (isHeading() || isLink() || isGenericFocusableElement())
1670        useTextUnderElement = true;
1671
1672    if (useTextUnderElement) {
1673        String text = textUnderElement();
1674        if (!text.isEmpty())
1675            textOrder.append(AccessibilityText(text, ChildrenText));
1676    }
1677}
1678
1679} // namespace WebCore
1680