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