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