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