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 Inc. ("Apple") nor the names of
14*     its contributors may be used to endorse or promote products derived
15*     from this software without specific prior written permission.
16*
17* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include "config.h"
30#include "AccessibilityNodeObject.h"
31
32#include "AXObjectCache.h"
33#include "AccessibilityImageMapLink.h"
34#include "AccessibilityListBox.h"
35#include "AccessibilitySpinButton.h"
36#include "AccessibilityTable.h"
37#include "ElementIterator.h"
38#include "EventNames.h"
39#include "FloatRect.h"
40#include "Frame.h"
41#include "FrameLoader.h"
42#include "FrameSelection.h"
43#include "FrameView.h"
44#include "HTMLAreaElement.h"
45#include "HTMLFieldSetElement.h"
46#include "HTMLFormElement.h"
47#include "HTMLFrameElementBase.h"
48#include "HTMLImageElement.h"
49#include "HTMLInputElement.h"
50#include "HTMLLabelElement.h"
51#include "HTMLLegendElement.h"
52#include "HTMLMapElement.h"
53#include "HTMLNames.h"
54#include "HTMLOptGroupElement.h"
55#include "HTMLOptionElement.h"
56#include "HTMLOptionsCollection.h"
57#include "HTMLParserIdioms.h"
58#include "HTMLPlugInImageElement.h"
59#include "HTMLSelectElement.h"
60#include "HTMLTextAreaElement.h"
61#include "HTMLTextFormControlElement.h"
62#include "HitTestRequest.h"
63#include "HitTestResult.h"
64#include "LabelableElement.h"
65#include "LocalizedStrings.h"
66#include "MathMLNames.h"
67#include "NodeList.h"
68#include "NodeTraversal.h"
69#include "Page.h"
70#include "ProgressTracker.h"
71#include "RenderImage.h"
72#include "RenderView.h"
73#include "SVGElement.h"
74#include "SVGNames.h"
75#include "Text.h"
76#include "TextControlInnerElements.h"
77#include "UserGestureIndicator.h"
78#include "VisibleUnits.h"
79#include "Widget.h"
80#include "htmlediting.h"
81#include <wtf/StdLibExtras.h>
82#include <wtf/text/StringBuilder.h>
83#include <wtf/unicode/CharacterNames.h>
84
85namespace WebCore {
86
87using namespace HTMLNames;
88
89static String accessibleNameForNode(Node*);
90
91AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
92    : AccessibilityObject()
93    , m_ariaRole(UnknownRole)
94    , m_childrenDirty(false)
95    , m_roleForMSAA(UnknownRole)
96#ifndef NDEBUG
97    , m_initialized(false)
98#endif
99    , m_node(node)
100{
101}
102
103AccessibilityNodeObject::~AccessibilityNodeObject()
104{
105    ASSERT(isDetached());
106}
107
108void AccessibilityNodeObject::init()
109{
110#ifndef NDEBUG
111    ASSERT(!m_initialized);
112    m_initialized = true;
113#endif
114    m_role = determineAccessibilityRole();
115}
116
117PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
118{
119    return adoptRef(new AccessibilityNodeObject(node));
120}
121
122void AccessibilityNodeObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
123{
124    // AccessibilityObject calls clearChildren.
125    AccessibilityObject::detach(detachmentType, cache);
126    m_node = 0;
127}
128
129void AccessibilityNodeObject::childrenChanged()
130{
131    // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
132    if (!node() && !renderer())
133        return;
134
135    AXObjectCache* cache = axObjectCache();
136    if (!cache)
137        return;
138    cache->postNotification(this, document(), AXObjectCache::AXChildrenChanged);
139
140    // Go up the accessibility parent chain, but only if the element already exists. This method is
141    // called during render layouts, minimal work should be done.
142    // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
143    // At the same time, process ARIA live region changes.
144    for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
145        parent->setNeedsToUpdateChildren();
146
147        // These notifications always need to be sent because screenreaders are reliant on them to perform.
148        // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
149
150        // If this element supports ARIA live regions, then notify the AT of changes.
151        if (parent->supportsARIALiveRegion())
152            cache->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged);
153
154        // If this element is an ARIA text control, notify the AT of changes.
155        if ((parent->isARIATextControl() || parent->hasContentEditableAttributeSet()) && !parent->isNativeTextControl())
156            cache->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged);
157    }
158}
159
160void AccessibilityNodeObject::updateAccessibilityRole()
161{
162    bool ignoredStatus = accessibilityIsIgnored();
163    m_role = determineAccessibilityRole();
164
165    // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
166    if (ignoredStatus != accessibilityIsIgnored())
167        childrenChanged();
168}
169
170AccessibilityObject* AccessibilityNodeObject::firstChild() const
171{
172    if (!node())
173        return 0;
174
175    Node* firstChild = node()->firstChild();
176
177    if (!firstChild)
178        return 0;
179
180    return axObjectCache()->getOrCreate(firstChild);
181}
182
183AccessibilityObject* AccessibilityNodeObject::lastChild() const
184{
185    if (!node())
186        return 0;
187
188    Node* lastChild = node()->lastChild();
189    if (!lastChild)
190        return 0;
191
192    return axObjectCache()->getOrCreate(lastChild);
193}
194
195AccessibilityObject* AccessibilityNodeObject::previousSibling() const
196{
197    if (!node())
198        return 0;
199
200    Node* previousSibling = node()->previousSibling();
201    if (!previousSibling)
202        return 0;
203
204    return axObjectCache()->getOrCreate(previousSibling);
205}
206
207AccessibilityObject* AccessibilityNodeObject::nextSibling() const
208{
209    if (!node())
210        return 0;
211
212    Node* nextSibling = node()->nextSibling();
213    if (!nextSibling)
214        return 0;
215
216    return axObjectCache()->getOrCreate(nextSibling);
217}
218
219AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
220{
221    return parentObject();
222}
223
224AccessibilityObject* AccessibilityNodeObject::parentObject() const
225{
226    if (!node())
227        return 0;
228
229    Node* parentObj = node()->parentNode();
230    if (!parentObj)
231        return nullptr;
232
233    if (AXObjectCache* cache = axObjectCache())
234        return cache->getOrCreate(parentObj);
235
236    return 0;
237}
238
239LayoutRect AccessibilityNodeObject::elementRect() const
240{
241    return boundingBoxRect();
242}
243
244LayoutRect AccessibilityNodeObject::boundingBoxRect() const
245{
246    // AccessibilityNodeObjects have no mechanism yet to return a size or position.
247    // For now, let's return the position of the ancestor that does have a position,
248    // 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.
249
250    LayoutRect boundingBox;
251
252    for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
253        if (positionProvider->isAccessibilityRenderObject()) {
254            LayoutRect parentRect = positionProvider->elementRect();
255            boundingBox.setSize(LayoutSize(parentRect.width(), LayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
256            boundingBox.setLocation(parentRect.location());
257            break;
258        }
259    }
260
261    return boundingBox;
262}
263
264void AccessibilityNodeObject::setNode(Node* node)
265{
266    m_node = node;
267}
268
269Document* AccessibilityNodeObject::document() const
270{
271    if (!node())
272        return 0;
273    return &node()->document();
274}
275
276AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
277{
278    if (!node())
279        return UnknownRole;
280
281    m_ariaRole = determineAriaRoleAttribute();
282
283    AccessibilityRole ariaRole = ariaRoleAttribute();
284    if (ariaRole != UnknownRole)
285        return ariaRole;
286
287    if (node()->isLink())
288        return WebCoreLinkRole;
289    if (node()->isTextNode())
290        return StaticTextRole;
291    if (node()->hasTagName(buttonTag))
292        return buttonRoleType();
293    if (isHTMLInputElement(node())) {
294        HTMLInputElement* input = toHTMLInputElement(node());
295        if (input->isCheckbox())
296            return CheckBoxRole;
297        if (input->isRadioButton())
298            return RadioButtonRole;
299        if (input->isTextButton())
300            return buttonRoleType();
301        if (input->isRangeControl())
302            return SliderRole;
303
304#if ENABLE(INPUT_TYPE_COLOR)
305        const AtomicString& type = input->getAttribute(typeAttr);
306        if (equalIgnoringCase(type, "color"))
307            return ColorWellRole;
308#endif
309
310        return TextFieldRole;
311    }
312    if (node()->hasTagName(selectTag)) {
313        HTMLSelectElement* selectElement = toHTMLSelectElement(node());
314        return selectElement->multiple() ? ListBoxRole : PopUpButtonRole;
315    }
316    if (isHTMLTextAreaElement(node()))
317        return TextAreaRole;
318    if (headingLevel())
319        return HeadingRole;
320    if (node()->hasTagName(divTag))
321        return DivRole;
322    if (node()->hasTagName(pTag))
323        return ParagraphRole;
324    if (isHTMLLabelElement(node()))
325        return LabelRole;
326    if (node()->isElementNode() && toElement(node())->isFocusable())
327        return GroupRole;
328
329    return UnknownRole;
330}
331
332void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index)
333{
334    if (!child)
335        return;
336
337    // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
338    // or its visibility has changed. In the latter case, this child may have a stale child cached.
339    // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
340    child->clearChildren();
341
342    if (child->accessibilityIsIgnored()) {
343        const auto& children = child->children();
344        size_t length = children.size();
345        for (size_t i = 0; i < length; ++i)
346            m_children.insert(index + i, children[i]);
347    } else {
348        ASSERT(child->parentObject() == this);
349        m_children.insert(index, child);
350    }
351}
352
353void AccessibilityNodeObject::addChild(AccessibilityObject* child)
354{
355    insertChild(child, m_children.size());
356}
357
358void AccessibilityNodeObject::addChildren()
359{
360    // If the need to add more children in addition to existing children arises,
361    // childrenChanged should have been called, leaving the object with no children.
362    ASSERT(!m_haveChildren);
363
364    if (!m_node)
365        return;
366
367    m_haveChildren = true;
368
369    // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
370    if (renderer() && !m_node->hasTagName(canvasTag))
371        return;
372
373    for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
374        addChild(axObjectCache()->getOrCreate(child));
375}
376
377bool AccessibilityNodeObject::canHaveChildren() const
378{
379    // If this is an AccessibilityRenderObject, then it's okay if this object
380    // doesn't have a node - there are some renderers that don't have associated
381    // nodes, like scroll areas and css-generated text.
382    if (!node() && !isAccessibilityRenderObject())
383        return false;
384
385    // When <noscript> is not being used (its renderer() == 0), ignore its children.
386    if (node() && !renderer() && node()->hasTagName(noscriptTag))
387        return false;
388
389    // Elements that should not have children
390    switch (roleValue()) {
391    case ImageRole:
392    case ButtonRole:
393    case PopUpButtonRole:
394    case CheckBoxRole:
395    case RadioButtonRole:
396    case TabRole:
397    case ToggleButtonRole:
398    case StaticTextRole:
399    case ListBoxOptionRole:
400    case ScrollBarRole:
401    case ProgressIndicatorRole:
402        return false;
403    case LegendRole:
404        if (Element* element = this->element())
405            return !ancestorsOfType<HTMLFieldSetElement>(*element).first();
406        FALLTHROUGH;
407    default:
408        return true;
409    }
410}
411
412bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
413{
414#ifndef NDEBUG
415    // Double-check that an AccessibilityObject is never accessed before
416    // it's been initialized.
417    ASSERT(m_initialized);
418#endif
419
420    // Handle non-rendered text that is exposed through aria-hidden=false.
421    if (m_node && m_node->isTextNode() && !renderer()) {
422        // Fallback content in iframe nodes should be ignored.
423        if (m_node->parentNode() && m_node->parentNode()->hasTagName(iframeTag) && m_node->parentNode()->renderer())
424            return true;
425
426        // Whitespace only text elements should be ignored when they have no renderer.
427        String string = stringValue().stripWhiteSpace().simplifyWhiteSpace();
428        if (!string.length())
429            return true;
430    }
431
432    AccessibilityObjectInclusion decision = defaultObjectInclusion();
433    if (decision == IncludeObject)
434        return false;
435    if (decision == IgnoreObject)
436        return true;
437    // If this element is within a parent that cannot have children, it should not be exposed.
438    if (isDescendantOfBarrenParent())
439        return true;
440
441    return m_role == UnknownRole;
442}
443
444bool AccessibilityNodeObject::canvasHasFallbackContent() const
445{
446    Node* node = this->node();
447    if (!node || !node->hasTagName(canvasTag))
448        return false;
449    Element& canvasElement = toElement(*node);
450    // If it has any children that are elements, we'll assume it might be fallback
451    // content. If it has no children or its only children are not elements
452    // (e.g. just text nodes), it doesn't have fallback content.
453    return childrenOfType<Element>(canvasElement).first();
454}
455
456bool AccessibilityNodeObject::isImageButton() const
457{
458    return isNativeImage() && isButton();
459}
460
461bool AccessibilityNodeObject::isAnchor() const
462{
463    return !isNativeImage() && isLink();
464}
465
466bool AccessibilityNodeObject::isNativeTextControl() const
467{
468    Node* node = this->node();
469    if (!node)
470        return false;
471
472    if (isHTMLTextAreaElement(node))
473        return true;
474
475    if (isHTMLInputElement(node)) {
476        HTMLInputElement* input = toHTMLInputElement(node);
477        return input->isText() || input->isNumberField();
478    }
479
480    return false;
481}
482
483bool AccessibilityNodeObject::isSearchField() const
484{
485    Node* node = this->node();
486    if (!node)
487        return false;
488
489    HTMLInputElement* inputElement = node->toInputElement();
490    if (!inputElement)
491        return false;
492
493    if (inputElement->isSearchField())
494        return true;
495
496    // Some websites don't label their search fields as such. However, they will
497    // use the word "search" in either the form or input type. This won't catch every case,
498    // but it will catch google.com for example.
499
500    // Check the node name of the input type, sometimes it's "search".
501    const AtomicString& nameAttribute = getAttribute(nameAttr);
502    if (nameAttribute.contains("search", false))
503        return true;
504
505    // Check the form action and the name, which will sometimes be "search".
506    HTMLFormElement* form = inputElement->form();
507    if (form && (form->name().contains("search", false) || form->action().contains("search", false)))
508        return true;
509
510    return false;
511}
512
513bool AccessibilityNodeObject::isNativeImage() const
514{
515    Node* node = this->node();
516    if (!node)
517        return false;
518
519    if (isHTMLImageElement(node))
520        return true;
521
522    if (node->hasTagName(appletTag) || node->hasTagName(embedTag) || node->hasTagName(objectTag))
523        return true;
524
525    if (isHTMLInputElement(node)) {
526        HTMLInputElement* input = toHTMLInputElement(node);
527        return input->isImageButton();
528    }
529
530    return false;
531}
532
533bool AccessibilityNodeObject::isImage() const
534{
535    return roleValue() == ImageRole;
536}
537
538bool AccessibilityNodeObject::isPasswordField() const
539{
540    Node* node = this->node();
541    if (!node || !node->isHTMLElement())
542        return false;
543
544    if (ariaRoleAttribute() != UnknownRole)
545        return false;
546
547    HTMLInputElement* inputElement = node->toInputElement();
548    if (!inputElement)
549        return false;
550
551    return inputElement->isPasswordField();
552}
553
554bool AccessibilityNodeObject::isInputImage() const
555{
556    Node* node = this->node();
557    if (!node)
558        return false;
559
560    if (roleValue() == ButtonRole && isHTMLInputElement(node)) {
561        HTMLInputElement* input = toHTMLInputElement(node);
562        return input->isImageButton();
563    }
564
565    return false;
566}
567
568bool AccessibilityNodeObject::isProgressIndicator() const
569{
570    return roleValue() == ProgressIndicatorRole;
571}
572
573bool AccessibilityNodeObject::isSlider() const
574{
575    return roleValue() == SliderRole;
576}
577
578bool AccessibilityNodeObject::isMenuRelated() const
579{
580    switch (roleValue()) {
581    case MenuRole:
582    case MenuBarRole:
583    case MenuButtonRole:
584    case MenuItemRole:
585    case MenuItemCheckboxRole:
586    case MenuItemRadioRole:
587        return true;
588    default:
589        return false;
590    }
591}
592
593bool AccessibilityNodeObject::isMenu() const
594{
595    return roleValue() == MenuRole;
596}
597
598bool AccessibilityNodeObject::isMenuBar() const
599{
600    return roleValue() == MenuBarRole;
601}
602
603bool AccessibilityNodeObject::isMenuButton() const
604{
605    return roleValue() == MenuButtonRole;
606}
607
608bool AccessibilityNodeObject::isMenuItem() const
609{
610    switch (roleValue()) {
611    case MenuItemRole:
612    case MenuItemRadioRole:
613    case MenuItemCheckboxRole:
614        return true;
615    default:
616        return false;
617    }
618}
619
620bool AccessibilityNodeObject::isNativeCheckboxOrRadio() const
621{
622    Node* node = this->node();
623    if (!node)
624        return false;
625
626    HTMLInputElement* input = node->toInputElement();
627    if (input)
628        return input->isCheckbox() || input->isRadioButton();
629
630    return false;
631}
632
633bool AccessibilityNodeObject::isEnabled() const
634{
635    // ARIA says that the disabled status applies to the current element and all descendant elements.
636    for (AccessibilityObject* object = const_cast<AccessibilityNodeObject*>(this); object; object = object->parentObject()) {
637        const AtomicString& disabledStatus = object->getAttribute(aria_disabledAttr);
638        if (equalIgnoringCase(disabledStatus, "true"))
639            return false;
640        if (equalIgnoringCase(disabledStatus, "false"))
641            break;
642    }
643
644    if (roleValue() == HorizontalRuleRole)
645        return false;
646
647    Node* node = this->node();
648    if (!node || !node->isElementNode())
649        return true;
650
651    return !toElement(node)->isDisabledFormControl();
652}
653
654bool AccessibilityNodeObject::isIndeterminate() const
655{
656    Node* node = this->node();
657    if (!node)
658        return false;
659
660    HTMLInputElement* inputElement = node->toInputElement();
661    if (!inputElement)
662        return false;
663
664    return inputElement->shouldAppearIndeterminate();
665}
666
667bool AccessibilityNodeObject::isPressed() const
668{
669    if (!isButton())
670        return false;
671
672    Node* node = this->node();
673    if (!node)
674        return false;
675
676    // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
677    if (ariaRoleAttribute() == ButtonRole) {
678        if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
679            return true;
680        return false;
681    }
682
683    if (!node->isElementNode())
684        return false;
685    return toElement(node)->active();
686}
687
688bool AccessibilityNodeObject::isChecked() const
689{
690    Node* node = this->node();
691    if (!node)
692        return false;
693
694    // First test for native checkedness semantics
695    HTMLInputElement* inputElement = node->toInputElement();
696    if (inputElement)
697        return inputElement->shouldAppearChecked();
698
699    // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
700    bool validRole = false;
701    switch (ariaRoleAttribute()) {
702    case RadioButtonRole:
703    case CheckBoxRole:
704    case MenuItemRole:
705    case MenuItemCheckboxRole:
706    case MenuItemRadioRole:
707        validRole = true;
708        break;
709    default:
710        break;
711    }
712
713    if (validRole && equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
714        return true;
715
716    return false;
717}
718
719bool AccessibilityNodeObject::isHovered() const
720{
721    Node* node = this->node();
722    if (!node)
723        return false;
724
725    return node->isElementNode() && toElement(node)->hovered();
726}
727
728bool AccessibilityNodeObject::isMultiSelectable() const
729{
730    const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
731    if (equalIgnoringCase(ariaMultiSelectable, "true"))
732        return true;
733    if (equalIgnoringCase(ariaMultiSelectable, "false"))
734        return false;
735
736    return node() && node()->hasTagName(selectTag) && toHTMLSelectElement(node())->multiple();
737}
738
739bool AccessibilityNodeObject::isReadOnly() const
740{
741    Node* node = this->node();
742    if (!node)
743        return true;
744
745    if (isHTMLTextAreaElement(node))
746        return toHTMLFormControlElement(node)->isReadOnly();
747
748    if (isHTMLInputElement(node)) {
749        HTMLInputElement* input = toHTMLInputElement(node);
750        if (input->isTextField())
751            return input->isReadOnly();
752    }
753
754    return !node->hasEditableStyle();
755}
756
757bool AccessibilityNodeObject::isRequired() const
758{
759    // Explicit aria-required values should trump native required attributes.
760    const AtomicString& requiredValue = getAttribute(aria_requiredAttr);
761    if (equalIgnoringCase(requiredValue, "true"))
762        return true;
763    if (equalIgnoringCase(requiredValue, "false"))
764        return false;
765
766    Node* n = this->node();
767    if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
768        return toHTMLFormControlElement(n)->isRequired();
769
770    return false;
771}
772
773bool AccessibilityNodeObject::supportsRequiredAttribute() const
774{
775    switch (roleValue()) {
776    case ButtonRole:
777        return isFileUploadButton();
778    case CellRole:
779    case CheckBoxRole:
780    case ComboBoxRole:
781    case GridRole:
782    case IncrementorRole:
783    case ListBoxRole:
784    case PopUpButtonRole:
785    case RadioButtonRole:
786    case RadioGroupRole:
787    case RowHeaderRole:
788    case SliderRole:
789    case SpinButtonRole:
790    case TableHeaderContainerRole:
791    case TextAreaRole:
792    case TextFieldRole:
793    case ToggleButtonRole:
794        return true;
795    default:
796        return false;
797    }
798}
799
800int AccessibilityNodeObject::headingLevel() const
801{
802    // headings can be in block flow and non-block flow
803    Node* node = this->node();
804    if (!node)
805        return false;
806
807    if (isHeading()) {
808        int ariaLevel = getAttribute(aria_levelAttr).toInt();
809        if (ariaLevel > 0)
810            return ariaLevel;
811    }
812
813    if (node->hasTagName(h1Tag))
814        return 1;
815
816    if (node->hasTagName(h2Tag))
817        return 2;
818
819    if (node->hasTagName(h3Tag))
820        return 3;
821
822    if (node->hasTagName(h4Tag))
823        return 4;
824
825    if (node->hasTagName(h5Tag))
826        return 5;
827
828    if (node->hasTagName(h6Tag))
829        return 6;
830
831    return 0;
832}
833
834String AccessibilityNodeObject::valueDescription() const
835{
836    if (!isRangeControl())
837        return String();
838
839    return getAttribute(aria_valuetextAttr).string();
840}
841
842float AccessibilityNodeObject::valueForRange() const
843{
844    if (node() && isHTMLInputElement(node())) {
845        HTMLInputElement* input = toHTMLInputElement(node());
846        if (input->isRangeControl())
847            return input->valueAsNumber();
848    }
849
850    if (!isRangeControl())
851        return 0.0f;
852
853    return getAttribute(aria_valuenowAttr).toFloat();
854}
855
856float AccessibilityNodeObject::maxValueForRange() const
857{
858    if (node() && isHTMLInputElement(node())) {
859        HTMLInputElement* input = toHTMLInputElement(node());
860        if (input->isRangeControl())
861            return input->maximum();
862    }
863
864    if (!isRangeControl())
865        return 0.0f;
866
867    return getAttribute(aria_valuemaxAttr).toFloat();
868}
869
870float AccessibilityNodeObject::minValueForRange() const
871{
872    if (node() && isHTMLInputElement(node())) {
873        HTMLInputElement* input = toHTMLInputElement(node());
874        if (input->isRangeControl())
875            return input->minimum();
876    }
877
878    if (!isRangeControl())
879        return 0.0f;
880
881    return getAttribute(aria_valueminAttr).toFloat();
882}
883
884float AccessibilityNodeObject::stepValueForRange() const
885{
886    return getAttribute(stepAttr).toFloat();
887}
888
889bool AccessibilityNodeObject::isHeading() const
890{
891    return roleValue() == HeadingRole;
892}
893
894bool AccessibilityNodeObject::isLink() const
895{
896    return roleValue() == WebCoreLinkRole;
897}
898
899bool AccessibilityNodeObject::isControl() const
900{
901    Node* node = this->node();
902    if (!node)
903        return false;
904
905    return ((node->isElementNode() && toElement(node)->isFormControlElement())
906        || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
907}
908
909bool AccessibilityNodeObject::isFieldset() const
910{
911    Node* node = this->node();
912    if (!node)
913        return false;
914
915    return node->hasTagName(fieldsetTag);
916}
917
918bool AccessibilityNodeObject::isGroup() const
919{
920    return roleValue() == GroupRole;
921}
922
923AccessibilityObject* AccessibilityNodeObject::selectedRadioButton()
924{
925    if (!isRadioGroup())
926        return nullptr;
927
928    // Find the child radio button that is selected (ie. the intValue == 1).
929    for (const auto& child : children()) {
930        if (child->roleValue() == RadioButtonRole && child->checkboxOrRadioValue() == ButtonStateOn)
931            return child.get();
932    }
933    return nullptr;
934}
935
936AccessibilityObject* AccessibilityNodeObject::selectedTabItem()
937{
938    if (!isTabList())
939        return nullptr;
940
941    // Find the child tab item that is selected (ie. the intValue == 1).
942    AccessibilityObject::AccessibilityChildrenVector tabs;
943    tabChildren(tabs);
944
945    for (const auto& child : children()) {
946        if (child->isTabItem() && child->isChecked())
947            return child.get();
948    }
949    return nullptr;
950}
951
952AccessibilityButtonState AccessibilityNodeObject::checkboxOrRadioValue() const
953{
954    if (isNativeCheckboxOrRadio())
955        return isChecked() ? ButtonStateOn : ButtonStateOff;
956
957    return AccessibilityObject::checkboxOrRadioValue();
958}
959
960Element* AccessibilityNodeObject::anchorElement() const
961{
962    Node* node = this->node();
963    if (!node)
964        return 0;
965
966    AXObjectCache* cache = axObjectCache();
967
968    // search up the DOM tree for an anchor element
969    // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
970    for ( ; node; node = node->parentNode()) {
971        if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
972            return toElement(node);
973    }
974
975    return 0;
976}
977
978static bool isNodeActionElement(Node* node)
979{
980    if (isHTMLInputElement(node)) {
981        HTMLInputElement* input = toHTMLInputElement(node);
982        if (!input->isDisabledFormControl() && (input->isRadioButton() || input->isCheckbox() || input->isTextButton() || input->isFileUpload() || input->isImageButton()))
983            return true;
984    } else if (node->hasTagName(buttonTag) || node->hasTagName(selectTag))
985        return true;
986
987    return false;
988}
989
990static Element* nativeActionElement(Node* start)
991{
992    if (!start)
993        return 0;
994
995    // Do a deep-dive to see if any nodes should be used as the action element.
996    // We have to look at Nodes, since this method should only be called on objects that do not have children (like buttons).
997    // It solves the problem when authors put role="button" on a group and leave the actual button inside the group.
998
999    for (Node* child = start->firstChild(); child; child = child->nextSibling()) {
1000        if (isNodeActionElement(child))
1001            return toElement(child);
1002
1003        if (Element* subChild = nativeActionElement(child))
1004            return subChild;
1005    }
1006    return 0;
1007}
1008
1009Element* AccessibilityNodeObject::actionElement() const
1010{
1011    Node* node = this->node();
1012    if (!node)
1013        return 0;
1014
1015    if (isNodeActionElement(node))
1016        return toElement(node);
1017
1018    if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
1019        return toElement(node);
1020
1021    switch (roleValue()) {
1022    case ButtonRole:
1023    case PopUpButtonRole:
1024    case ToggleButtonRole:
1025    case TabRole:
1026    case MenuItemRole:
1027    case MenuItemCheckboxRole:
1028    case MenuItemRadioRole:
1029    case ListItemRole:
1030        // Check if the author is hiding the real control element inside the ARIA element.
1031        if (Element* nativeElement = nativeActionElement(node))
1032            return nativeElement;
1033        return toElement(node);
1034    default:
1035        break;
1036    }
1037
1038    Element* elt = anchorElement();
1039    if (!elt)
1040        elt = mouseButtonListener();
1041    return elt;
1042}
1043
1044Element* AccessibilityNodeObject::mouseButtonListener(MouseButtonListenerResultFilter filter) const
1045{
1046    Node* node = this->node();
1047    if (!node)
1048        return 0;
1049
1050    // check if our parent is a mouse button listener
1051    // FIXME: Do the continuation search like anchorElement does
1052    for (auto& element : elementLineage(node->isElementNode() ? toElement(node) : node->parentElement())) {
1053        // If we've reached the body and this is not a control element, do not expose press action for this element unless filter is IncludeBodyElement.
1054        // It can cause false positives, where every piece of text is labeled as accepting press actions.
1055        if (element.hasTagName(bodyTag) && isStaticText() && filter == ExcludeBodyElement)
1056            break;
1057
1058        if (element.hasEventListeners(eventNames().clickEvent) || element.hasEventListeners(eventNames().mousedownEvent) || element.hasEventListeners(eventNames().mouseupEvent))
1059            return &element;
1060    }
1061
1062    return 0;
1063}
1064
1065bool AccessibilityNodeObject::isDescendantOfBarrenParent() const
1066{
1067    for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
1068        if (!object->canHaveChildren())
1069            return true;
1070    }
1071
1072    return false;
1073}
1074
1075void AccessibilityNodeObject::alterSliderValue(bool increase)
1076{
1077    if (roleValue() != SliderRole)
1078        return;
1079
1080    if (!getAttribute(stepAttr).isEmpty())
1081        changeValueByStep(increase);
1082    else
1083        changeValueByPercent(increase ? 5 : -5);
1084}
1085
1086void AccessibilityNodeObject::increment()
1087{
1088    UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
1089    alterSliderValue(true);
1090}
1091
1092void AccessibilityNodeObject::decrement()
1093{
1094    UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
1095    alterSliderValue(false);
1096}
1097
1098void AccessibilityNodeObject::changeValueByStep(bool increase)
1099{
1100    float step = stepValueForRange();
1101    float value = valueForRange();
1102
1103    value += increase ? step : -step;
1104
1105    setValue(String::number(value));
1106
1107    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged);
1108}
1109
1110void AccessibilityNodeObject::changeValueByPercent(float percentChange)
1111{
1112    float range = maxValueForRange() - minValueForRange();
1113    float step = range * (percentChange / 100);
1114    float value = valueForRange();
1115
1116    // Make sure the specified percent will cause a change of one integer step or larger.
1117    if (fabs(step) < 1)
1118        step = fabs(percentChange) * (1 / percentChange);
1119
1120    value += step;
1121    setValue(String::number(value));
1122
1123    axObjectCache()->postNotification(node(), AXObjectCache::AXValueChanged);
1124}
1125
1126bool AccessibilityNodeObject::isGenericFocusableElement() const
1127{
1128    if (!canSetFocusAttribute())
1129        return false;
1130
1131    // If it's a control, it's not generic.
1132    if (isControl())
1133        return false;
1134
1135    AccessibilityRole role = roleValue();
1136    if (role == VideoRole || role == AudioRole)
1137        return false;
1138
1139    // If it has an aria role, it's not generic.
1140    if (m_ariaRole != UnknownRole)
1141        return false;
1142
1143    // If the content editable attribute is set on this element, that's the reason
1144    // it's focusable, and existing logic should handle this case already - so it's not a
1145    // generic focusable element.
1146
1147    if (hasContentEditableAttributeSet())
1148        return false;
1149
1150    // The web area and body element are both focusable, but existing logic handles these
1151    // cases already, so we don't need to include them here.
1152    if (role == WebAreaRole)
1153        return false;
1154    if (node() && node()->hasTagName(bodyTag))
1155        return false;
1156
1157    // An SVG root is focusable by default, but it's probably not interactive, so don't
1158    // include it. It can still be made accessible by giving it an ARIA role.
1159    if (role == SVGRootRole)
1160        return false;
1161
1162    return true;
1163}
1164
1165HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const
1166{
1167    if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
1168        return 0;
1169
1170    const AtomicString& id = element->getIdAttribute();
1171    if (!id.isEmpty()) {
1172        if (HTMLLabelElement* label = element->treeScope().labelElementForId(id))
1173            return label;
1174    }
1175
1176    return ancestorsOfType<HTMLLabelElement>(*element).first();
1177}
1178
1179String AccessibilityNodeObject::ariaAccessibilityDescription() const
1180{
1181    String ariaLabeledBy = ariaLabeledByAttribute();
1182    if (!ariaLabeledBy.isEmpty())
1183        return ariaLabeledBy;
1184
1185    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1186    if (!ariaLabel.isEmpty())
1187        return ariaLabel;
1188
1189    return String();
1190}
1191
1192static Element* siblingWithAriaRole(String role, Node* node)
1193{
1194    ContainerNode* parent = node->parentNode();
1195    if (!parent)
1196        return nullptr;
1197
1198    for (auto& sibling : childrenOfType<Element>(*parent)) {
1199        const AtomicString& siblingAriaRole = sibling.fastGetAttribute(roleAttr);
1200        if (equalIgnoringCase(siblingAriaRole, role))
1201            return &sibling;
1202    }
1203
1204    return nullptr;
1205}
1206
1207Element* AccessibilityNodeObject::menuElementForMenuButton() const
1208{
1209    if (ariaRoleAttribute() != MenuButtonRole)
1210        return nullptr;
1211
1212    return siblingWithAriaRole("menu", node());
1213}
1214
1215AccessibilityObject* AccessibilityNodeObject::menuForMenuButton() const
1216{
1217    if (AXObjectCache* cache = axObjectCache())
1218        return cache->getOrCreate(menuElementForMenuButton());
1219    return nullptr;
1220}
1221
1222Element* AccessibilityNodeObject::menuItemElementForMenu() const
1223{
1224    if (ariaRoleAttribute() != MenuRole)
1225        return 0;
1226
1227    return siblingWithAriaRole("menuitem", node());
1228}
1229
1230AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const
1231{
1232    AXObjectCache* cache = axObjectCache();
1233    if (!cache)
1234        return nullptr;
1235
1236    Element* menuItem = menuItemElementForMenu();
1237
1238    if (menuItem) {
1239        // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
1240        AccessibilityObject* menuItemAX = cache->getOrCreate(menuItem);
1241        if (menuItemAX && menuItemAX->isMenuButton())
1242            return menuItemAX;
1243    }
1244    return 0;
1245}
1246
1247bool AccessibilityNodeObject::usesAltTagForTextComputation() const
1248{
1249    return isImage() || isInputImage() || isNativeImage() || isCanvas() || (node() && node()->hasTagName(imgTag));
1250}
1251
1252void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder) const
1253{
1254    Node* node = this->node();
1255    if (!node)
1256        return;
1257
1258    bool isInputTag = isHTMLInputElement(node);
1259    if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1260        HTMLLabelElement* label = labelForElement(toElement(node));
1261        if (label) {
1262            AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
1263            String innerText = label->innerText();
1264            // Only use the <label> text if there's no ARIA override.
1265            if (!innerText.isEmpty() && !ariaAccessibilityDescription())
1266                textOrder.append(AccessibilityText(innerText, LabelByElementText, labelObject));
1267            return;
1268        }
1269    }
1270
1271    AccessibilityObject* titleUIElement = this->titleUIElement();
1272    if (titleUIElement)
1273        textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement));
1274}
1275
1276void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
1277{
1278    if (isWebArea()) {
1279        String webAreaText = alternativeTextForWebArea();
1280        if (!webAreaText.isEmpty())
1281            textOrder.append(AccessibilityText(webAreaText, AlternativeText));
1282        return;
1283    }
1284
1285    ariaLabeledByText(textOrder);
1286
1287    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1288    if (!ariaLabel.isEmpty())
1289        textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
1290
1291    if (usesAltTagForTextComputation()) {
1292        if (renderer() && renderer()->isRenderImage()) {
1293            String renderAltText = toRenderImage(renderer())->altText();
1294
1295            // RenderImage will return title as a fallback from altText, but we don't want title here because we consider that in helpText.
1296            if (!renderAltText.isEmpty() && renderAltText != getAttribute(titleAttr)) {
1297                textOrder.append(AccessibilityText(renderAltText, AlternativeText));
1298                return;
1299            }
1300        }
1301        // Images should use alt as long as the attribute is present, even if empty.
1302        // Otherwise, it should fallback to other methods, like the title attribute.
1303        const AtomicString& alt = getAttribute(altAttr);
1304        if (!alt.isEmpty())
1305            textOrder.append(AccessibilityText(alt, AlternativeText));
1306    }
1307
1308    Node* node = this->node();
1309    if (!node)
1310        return;
1311
1312    // The fieldset element derives its alternative text from the first associated legend element if one is available.
1313    if (isHTMLFieldSetElement(node)) {
1314        AccessibilityObject* object = axObjectCache()->getOrCreate(toHTMLFieldSetElement(node)->legend());
1315        if (object && !object->isHidden())
1316            textOrder.append(AccessibilityText(accessibleNameForNode(object->node()), AlternativeText));
1317    }
1318
1319    // SVG elements all can have a <svg:title> element inside which should act as the descriptive text.
1320    if (node->isSVGElement())
1321        textOrder.append(AccessibilityText(toSVGElement(node)->title(), AlternativeText));
1322
1323#if ENABLE(MATHML)
1324    if (node->isMathMLElement())
1325        textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText));
1326#endif
1327}
1328
1329void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder) const
1330{
1331    Node* node = this->node();
1332    if (!node)
1333        return;
1334
1335    bool isInputTag = isHTMLInputElement(node);
1336    if (isInputTag) {
1337        HTMLInputElement* input = toHTMLInputElement(node);
1338        if (input->isTextButton()) {
1339            textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText));
1340            return;
1341        }
1342    }
1343
1344    // If this node isn't rendered, there's no inner text we can extract from a select element.
1345    if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1346        return;
1347
1348    bool useTextUnderElement = false;
1349
1350    switch (roleValue()) {
1351    case PopUpButtonRole:
1352        // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1353        if (node->hasTagName(selectTag))
1354            break;
1355        FALLTHROUGH;
1356    case ButtonRole:
1357    case ToggleButtonRole:
1358    case CheckBoxRole:
1359    case ListBoxOptionRole:
1360    // MacOS does not expect native <li> elements to expose label information, it only expects leaf node elements to do that.
1361#if !PLATFORM(COCOA)
1362    case ListItemRole:
1363#endif
1364    case MenuButtonRole:
1365    case MenuItemRole:
1366    case MenuItemCheckboxRole:
1367    case MenuItemRadioRole:
1368    case RadioButtonRole:
1369    case TabRole:
1370    case ProgressIndicatorRole:
1371        useTextUnderElement = true;
1372        break;
1373    default:
1374        break;
1375    }
1376
1377    // If it's focusable but it's not content editable or a known control type, then it will appear to
1378    // the user as a single atomic object, so we should use its text as the default title.
1379    if (isHeading() || isLink())
1380        useTextUnderElement = true;
1381
1382    if (useTextUnderElement) {
1383        AccessibilityTextUnderElementMode mode;
1384
1385        // Headings often include links as direct children. Those links need to be included in text under element.
1386        if (isHeading())
1387            mode.includeFocusableContent = true;
1388
1389        String text = textUnderElement(mode);
1390        if (!text.isEmpty())
1391            textOrder.append(AccessibilityText(text, ChildrenText));
1392    }
1393}
1394
1395void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) const
1396{
1397    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1398    if (!ariaHelp.isEmpty())
1399        textOrder.append(AccessibilityText(ariaHelp, HelpText));
1400
1401    String describedBy = ariaDescribedByAttribute();
1402    if (!describedBy.isEmpty())
1403        textOrder.append(AccessibilityText(describedBy, SummaryText));
1404
1405    // Summary attribute used as help text on tables.
1406    const AtomicString& summary = getAttribute(summaryAttr);
1407    if (!summary.isEmpty())
1408        textOrder.append(AccessibilityText(summary, SummaryText));
1409
1410    // The title attribute should be used as help text unless it is already being used as descriptive text.
1411    const AtomicString& title = getAttribute(titleAttr);
1412    if (!title.isEmpty())
1413        textOrder.append(AccessibilityText(title, TitleTagText));
1414}
1415
1416void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textOrder)
1417{
1418    titleElementText(textOrder);
1419    alternativeText(textOrder);
1420    visibleText(textOrder);
1421    helpText(textOrder);
1422
1423    String placeholder = placeholderValue();
1424    if (!placeholder.isEmpty())
1425        textOrder.append(AccessibilityText(placeholder, PlaceholderText));
1426}
1427
1428void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
1429{
1430    String ariaLabeledBy = ariaLabeledByAttribute();
1431    if (!ariaLabeledBy.isEmpty()) {
1432        Vector<Element*> elements;
1433        ariaLabeledByElements(elements);
1434
1435        Vector<RefPtr<AccessibilityObject>> axElements;
1436        for (const auto& element : elements) {
1437            RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(element);
1438            axElements.append(axElement);
1439        }
1440
1441        textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, WTF::move(axElements)));
1442    }
1443}
1444
1445String AccessibilityNodeObject::alternativeTextForWebArea() const
1446{
1447    // The WebArea description should follow this order:
1448    //     aria-label on the <html>
1449    //     title on the <html>
1450    //     <title> inside the <head> (of it was set through JS)
1451    //     name on the <html>
1452    // For iframes:
1453    //     aria-label on the <iframe>
1454    //     title on the <iframe>
1455    //     name on the <iframe>
1456
1457    Document* document = this->document();
1458    if (!document)
1459        return String();
1460
1461    // Check if the HTML element has an aria-label for the webpage.
1462    if (Element* documentElement = document->documentElement()) {
1463        const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
1464        if (!ariaLabel.isEmpty())
1465            return ariaLabel;
1466    }
1467
1468    Node* owner = document->ownerElement();
1469    if (owner) {
1470        if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1471            const AtomicString& title = toElement(owner)->getAttribute(titleAttr);
1472            if (!title.isEmpty())
1473                return title;
1474            return toElement(owner)->getNameAttribute();
1475        }
1476        if (owner->isHTMLElement())
1477            return toHTMLElement(owner)->getNameAttribute();
1478    }
1479
1480    String documentTitle = document->title();
1481    if (!documentTitle.isEmpty())
1482        return documentTitle;
1483
1484    owner = document->body();
1485    if (owner && owner->isHTMLElement())
1486        return toHTMLElement(owner)->getNameAttribute();
1487
1488    return String();
1489}
1490
1491String AccessibilityNodeObject::accessibilityDescription() const
1492{
1493    // Static text should not have a description, it should only have a stringValue.
1494    if (roleValue() == StaticTextRole)
1495        return String();
1496
1497    String ariaDescription = ariaAccessibilityDescription();
1498    if (!ariaDescription.isEmpty())
1499        return ariaDescription;
1500
1501    if (usesAltTagForTextComputation()) {
1502        // Images should use alt as long as the attribute is present, even if empty.
1503        // Otherwise, it should fallback to other methods, like the title attribute.
1504        const AtomicString& alt = getAttribute(altAttr);
1505        if (!alt.isNull())
1506            return alt;
1507    }
1508
1509    // SVG elements all can have a <svg:title> element inside which should act as the descriptive text.
1510    if (m_node && m_node->isSVGElement())
1511        return toSVGElement(m_node)->title();
1512
1513#if ENABLE(MATHML)
1514    if (m_node && m_node->isMathMLElement())
1515        return getAttribute(MathMLNames::alttextAttr);
1516#endif
1517
1518    // An element's descriptive text is comprised of title() (what's visible on the screen) and accessibilityDescription() (other descriptive text).
1519    // Both are used to generate what a screen reader speaks.
1520    // If this point is reached (i.e. there's no accessibilityDescription) and there's no title(), we should fallback to using the title attribute.
1521    // 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).
1522    if (title().isEmpty())
1523        return getAttribute(titleAttr);
1524
1525    return String();
1526}
1527
1528String AccessibilityNodeObject::helpText() const
1529{
1530    Node* node = this->node();
1531    if (!node)
1532        return String();
1533
1534    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1535    if (!ariaHelp.isEmpty())
1536        return ariaHelp;
1537
1538    String describedBy = ariaDescribedByAttribute();
1539    if (!describedBy.isEmpty())
1540        return describedBy;
1541
1542    String description = accessibilityDescription();
1543    for (Node* curr = node; curr; curr = curr->parentNode()) {
1544        if (curr->isHTMLElement()) {
1545            const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
1546            if (!summary.isEmpty())
1547                return summary;
1548
1549            // The title attribute should be used as help text unless it is already being used as descriptive text.
1550            const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
1551            if (!title.isEmpty() && description != title)
1552                return title;
1553        }
1554
1555        // Only take help text from an ancestor element if its a group or an unknown role. If help was
1556        // added to those kinds of elements, it is likely it was meant for a child element.
1557        AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
1558        if (axObj) {
1559            AccessibilityRole role = axObj->roleValue();
1560            if (role != GroupRole && role != UnknownRole)
1561                break;
1562        }
1563    }
1564
1565    return String();
1566}
1567
1568unsigned AccessibilityNodeObject::hierarchicalLevel() const
1569{
1570    Node* node = this->node();
1571    if (!node || !node->isElementNode())
1572        return 0;
1573    Element* element = toElement(node);
1574    String ariaLevel = element->getAttribute(aria_levelAttr);
1575    if (!ariaLevel.isEmpty())
1576        return ariaLevel.toInt();
1577
1578    // Only tree item will calculate its level through the DOM currently.
1579    if (roleValue() != TreeItemRole)
1580        return 0;
1581
1582    // Hierarchy leveling starts at 1, to match the aria-level spec.
1583    // We measure tree hierarchy by the number of groups that the item is within.
1584    unsigned level = 1;
1585    for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1586        AccessibilityRole parentRole = parent->roleValue();
1587        if (parentRole == GroupRole)
1588            level++;
1589        else if (parentRole == TreeRole)
1590            break;
1591    }
1592
1593    return level;
1594}
1595
1596// When building the textUnderElement for an object, determine whether or not
1597// we should include the inner text of this given descendant object or skip it.
1598static bool shouldUseAccessiblityObjectInnerText(AccessibilityObject* obj, AccessibilityTextUnderElementMode mode)
1599{
1600    // Do not use any heuristic if we are explicitly asking to include all the children.
1601    if (mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)
1602        return true;
1603
1604    // Consider this hypothetical example:
1605    // <div tabindex=0>
1606    //   <h2>
1607    //     Table of contents
1608    //   </h2>
1609    //   <a href="#start">Jump to start of book</a>
1610    //   <ul>
1611    //     <li><a href="#1">Chapter 1</a></li>
1612    //     <li><a href="#1">Chapter 2</a></li>
1613    //   </ul>
1614    // </div>
1615    //
1616    // The goal is to return a reasonable title for the outer container div, because
1617    // it's focusable - but without making its title be the full inner text, which is
1618    // quite long. As a heuristic, skip links, controls, and elements that are usually
1619    // containers with lots of children.
1620
1621    if (equalIgnoringCase(obj->getAttribute(aria_hiddenAttr), "true"))
1622        return false;
1623
1624    // If something doesn't expose any children, then we can always take the inner text content.
1625    // This is what we want when someone puts an <a> inside a <button> for example.
1626    if (obj->isDescendantOfBarrenParent())
1627        return true;
1628
1629    // Skip focusable children, so we don't include the text of links and controls.
1630    if (obj->canSetFocusAttribute() && !mode.includeFocusableContent)
1631        return false;
1632
1633    // Skip big container elements like lists, tables, etc.
1634    if (obj->isList() || obj->isAccessibilityTable() || obj->isTree() || obj->isCanvas())
1635        return false;
1636
1637    return true;
1638}
1639
1640static bool shouldAddSpaceBeforeAppendingNextElement(StringBuilder& builder, String& childText)
1641{
1642    if (!builder.length() || !childText.length())
1643        return false;
1644
1645    // We don't need to add an additional space before or after a line break.
1646    return !(isHTMLLineBreak(childText[0]) || isHTMLLineBreak(builder[builder.length() - 1]));
1647}
1648
1649String AccessibilityNodeObject::textUnderElement(AccessibilityTextUnderElementMode mode) const
1650{
1651    Node* node = this->node();
1652    if (node && node->isTextNode())
1653        return toText(node)->wholeText();
1654
1655    // The render tree should be stable before going ahead. Otherwise, further uses of the
1656    // TextIterator will force a layout update, potentially altering the accessibility tree
1657    // and leading to crashes in the loop that computes the result text from the children.
1658    ASSERT(!document()->renderView()->layoutState());
1659    ASSERT(!document()->childNeedsStyleRecalc());
1660
1661    StringBuilder builder;
1662    for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
1663        if (!shouldUseAccessiblityObjectInnerText(child, mode))
1664            continue;
1665
1666        if (child->isAccessibilityNodeObject()) {
1667            Vector<AccessibilityText> textOrder;
1668            toAccessibilityNodeObject(child)->alternativeText(textOrder);
1669            if (textOrder.size() > 0 && textOrder[0].text.length()) {
1670                if (shouldAddSpaceBeforeAppendingNextElement(builder, textOrder[0].text))
1671                    builder.append(' ');
1672                builder.append(textOrder[0].text);
1673                continue;
1674            }
1675        }
1676
1677        String childText = child->textUnderElement(mode);
1678        if (childText.length()) {
1679            if (shouldAddSpaceBeforeAppendingNextElement(builder, childText))
1680                builder.append(' ');
1681            builder.append(childText);
1682        }
1683    }
1684
1685    return builder.toString().stripWhiteSpace().simplifyWhiteSpace(isHTMLSpaceButNotLineBreak);
1686}
1687
1688String AccessibilityNodeObject::title() const
1689{
1690    Node* node = this->node();
1691    if (!node)
1692        return String();
1693
1694    bool isInputTag = isHTMLInputElement(node);
1695    if (isInputTag) {
1696        HTMLInputElement* input = toHTMLInputElement(node);
1697        if (input->isTextButton())
1698            return input->valueWithDefault();
1699    }
1700
1701    if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
1702        HTMLLabelElement* label = labelForElement(toElement(node));
1703        // Use the label text as the title if 1) the title element is NOT an exposed element and 2) there's no ARIA override.
1704        if (label && !exposesTitleUIElement() && !ariaAccessibilityDescription().length())
1705            return label->innerText();
1706    }
1707
1708    // If this node isn't rendered, there's no inner text we can extract from a select element.
1709    if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
1710        return String();
1711
1712    switch (roleValue()) {
1713    case PopUpButtonRole:
1714        // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1715        if (node->hasTagName(selectTag))
1716            return String();
1717        FALLTHROUGH;
1718    case ButtonRole:
1719    case ToggleButtonRole:
1720    case CheckBoxRole:
1721    case ListBoxOptionRole:
1722    case ListItemRole:
1723    case MenuButtonRole:
1724    case MenuItemRole:
1725    case MenuItemCheckboxRole:
1726    case MenuItemRadioRole:
1727    case RadioButtonRole:
1728    case TabRole:
1729        return textUnderElement();
1730    // SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
1731    case SVGRootRole:
1732        return String();
1733    default:
1734        break;
1735    }
1736
1737    if (isLink())
1738        return textUnderElement();
1739    if (isHeading())
1740        return textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeSkipIgnoredChildren, true));
1741
1742    return String();
1743}
1744
1745String AccessibilityNodeObject::text() const
1746{
1747    // If this is a user defined static text, use the accessible name computation.
1748    if (ariaRoleAttribute() == StaticTextRole) {
1749        Vector<AccessibilityText> textOrder;
1750        alternativeText(textOrder);
1751        if (textOrder.size() > 0 && textOrder[0].text.length())
1752            return textOrder[0].text;
1753    }
1754
1755    if (!isTextControl())
1756        return String();
1757
1758    Node* node = this->node();
1759    if (!node)
1760        return String();
1761
1762    if (isNativeTextControl() && (isHTMLTextAreaElement(node) || isHTMLInputElement(node)))
1763        return toHTMLTextFormControlElement(node)->value();
1764
1765    if (!node->isElementNode())
1766        return String();
1767
1768    return toElement(node)->innerText();
1769}
1770
1771String AccessibilityNodeObject::stringValue() const
1772{
1773    Node* node = this->node();
1774    if (!node)
1775        return String();
1776
1777    if (ariaRoleAttribute() == StaticTextRole) {
1778        String staticText = text();
1779        if (!staticText.length())
1780            staticText = textUnderElement();
1781        return staticText;
1782    }
1783
1784    if (node->isTextNode())
1785        return textUnderElement();
1786
1787    if (node->hasTagName(selectTag)) {
1788        HTMLSelectElement* selectElement = toHTMLSelectElement(node);
1789        int selectedIndex = selectElement->selectedIndex();
1790        const Vector<HTMLElement*>& listItems = selectElement->listItems();
1791        if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
1792            const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
1793            if (!overriddenDescription.isNull())
1794                return overriddenDescription;
1795        }
1796        if (!selectElement->multiple())
1797            return selectElement->value();
1798        return String();
1799    }
1800
1801    if (isTextControl())
1802        return text();
1803
1804    // FIXME: We might need to implement a value here for more types
1805    // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
1806    // this would require subclassing or making accessibilityAttributeNames do something other than return a
1807    // single static array.
1808    return String();
1809}
1810
1811void AccessibilityNodeObject::colorValue(int& r, int& g, int& b) const
1812{
1813    r = 0;
1814    g = 0;
1815    b = 0;
1816
1817    if (!isColorWell())
1818        return;
1819
1820    if (!node() || !isHTMLInputElement(node()))
1821        return;
1822
1823    HTMLInputElement* input = toHTMLInputElement(node());
1824    const AtomicString& type = input->getAttribute(typeAttr);
1825    if (!equalIgnoringCase(type, "color"))
1826        return;
1827
1828    // HTMLInputElement::value always returns a string parseable by Color().
1829    Color color(input->value());
1830    r = color.red();
1831    g = color.green();
1832    b = color.blue();
1833}
1834
1835// This function implements the ARIA accessible name as described by the Mozilla
1836// ARIA Implementer's Guide.
1837static String accessibleNameForNode(Node* node)
1838{
1839    ASSERT(node);
1840    if (!node || !node->isElementNode())
1841        return String();
1842
1843    Element* element = toElement(node);
1844    const AtomicString& ariaLabel = element->fastGetAttribute(aria_labelAttr);
1845    if (!ariaLabel.isEmpty())
1846        return ariaLabel;
1847
1848    const AtomicString& alt = element->fastGetAttribute(altAttr);
1849    if (!alt.isEmpty())
1850        return alt;
1851
1852    if (isHTMLInputElement(node))
1853        return toHTMLInputElement(node)->value();
1854
1855    // If the node can be turned into an AX object, we can use standard name computation rules.
1856    // If however, the node cannot (because there's no renderer e.g.) fallback to using the basic text underneath.
1857    AccessibilityObject* axObject = node->document().axObjectCache()->getOrCreate(node);
1858    String text;
1859    if (axObject)
1860        text = axObject->textUnderElement();
1861    else
1862        text = element->innerText();
1863
1864    if (!text.isEmpty())
1865        return text;
1866
1867    const AtomicString& title = element->fastGetAttribute(titleAttr);
1868    if (!title.isEmpty())
1869        return title;
1870
1871    return String();
1872}
1873
1874String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
1875{
1876    StringBuilder builder;
1877    unsigned size = elements.size();
1878    for (unsigned i = 0; i < size; ++i) {
1879        if (i)
1880            builder.append(' ');
1881
1882        builder.append(accessibleNameForNode(elements[i]));
1883    }
1884    return builder.toString();
1885}
1886
1887String AccessibilityNodeObject::ariaDescribedByAttribute() const
1888{
1889    Vector<Element*> elements;
1890    elementsFromAttribute(elements, aria_describedbyAttr);
1891
1892    return accessibilityDescriptionForElements(elements);
1893}
1894
1895void AccessibilityNodeObject::ariaLabeledByElements(Vector<Element*>& elements) const
1896{
1897    elementsFromAttribute(elements, aria_labelledbyAttr);
1898    if (!elements.size())
1899        elementsFromAttribute(elements, aria_labeledbyAttr);
1900}
1901
1902
1903String AccessibilityNodeObject::ariaLabeledByAttribute() const
1904{
1905    Vector<Element*> elements;
1906    ariaLabeledByElements(elements);
1907
1908    return accessibilityDescriptionForElements(elements);
1909}
1910
1911bool AccessibilityNodeObject::hasAttributesRequiredForInclusion() const
1912{
1913    if (AccessibilityObject::hasAttributesRequiredForInclusion())
1914        return true;
1915
1916    if (!ariaAccessibilityDescription().isEmpty())
1917        return true;
1918
1919    return false;
1920}
1921
1922bool AccessibilityNodeObject::canSetFocusAttribute() const
1923{
1924    Node* node = this->node();
1925    if (!node)
1926        return false;
1927
1928    if (isWebArea())
1929        return true;
1930
1931    // NOTE: It would be more accurate to ask the document whether setFocusedElement() would
1932    // do anything. For example, setFocusedElement() will do nothing if the current focused
1933    // node will not relinquish the focus.
1934    if (!node)
1935        return false;
1936
1937    if (!node->isElementNode())
1938        return false;
1939
1940    Element* element = toElement(node);
1941
1942    if (element->isDisabledFormControl())
1943        return false;
1944
1945    return element->supportsFocus();
1946}
1947
1948AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
1949{
1950    const AtomicString& ariaRole = getAttribute(roleAttr);
1951    if (ariaRole.isNull() || ariaRole.isEmpty())
1952        return UnknownRole;
1953
1954    AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
1955
1956    // ARIA states if an item can get focus, it should not be presentational.
1957    if (role == PresentationalRole && canSetFocusAttribute())
1958        return UnknownRole;
1959
1960    if (role == ButtonRole)
1961        role = buttonRoleType();
1962
1963    if (role == TextAreaRole && !ariaIsMultiline())
1964        role = TextFieldRole;
1965
1966    role = remapAriaRoleDueToParent(role);
1967
1968    // Presentational roles are invalidated by the presence of ARIA attributes.
1969    if (role == PresentationalRole && supportsARIAAttributes())
1970        role = UnknownRole;
1971
1972    if (role)
1973        return role;
1974
1975    return UnknownRole;
1976}
1977
1978AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
1979{
1980    return m_ariaRole;
1981}
1982
1983AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
1984{
1985    // Some objects change their role based on their parent.
1986    // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop.
1987    // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
1988    // https://bugs.webkit.org/show_bug.cgi?id=65174
1989
1990    if (role != ListBoxOptionRole && role != MenuItemRole)
1991        return role;
1992
1993    for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
1994        AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
1995
1996        // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
1997        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
1998            return MenuItemRole;
1999        // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
2000        if (role == MenuItemRole && parentAriaRole == GroupRole)
2001            return MenuButtonRole;
2002
2003        // If the parent had a different role, then we don't need to continue searching up the chain.
2004        if (parentAriaRole)
2005            break;
2006    }
2007
2008    return role;
2009}
2010
2011bool AccessibilityNodeObject::canSetSelectedAttribute() const
2012{
2013    // Elements that can be selected
2014    switch (roleValue()) {
2015    case CellRole:
2016    case RadioButtonRole:
2017    case RowHeaderRole:
2018    case RowRole:
2019    case TabListRole:
2020    case TabRole:
2021    case TreeGridRole:
2022    case TreeItemRole:
2023    case TreeRole:
2024        return isEnabled();
2025    default:
2026        return false;
2027    }
2028}
2029
2030} // namespace WebCore
2031