2 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "AccessibilityRenderObject.h"
32 #include "AXObjectCache.h"
33 #include "AccessibilityImageMapLink.h"
34 #include "AccessibilityListBox.h"
35 #include "EventNames.h"
36 #include "FloatRect.h"
38 #include "FrameLoader.h"
39 #include "FrameSelection.h"
40 #include "HTMLAreaElement.h"
41 #include "HTMLFormElement.h"
42 #include "HTMLFrameElementBase.h"
43 #include "HTMLImageElement.h"
44 #include "HTMLInputElement.h"
45 #include "HTMLLabelElement.h"
46 #include "HTMLMapElement.h"
47 #include "HTMLNames.h"
48 #include "HTMLOptGroupElement.h"
49 #include "HTMLOptionElement.h"
50 #include "HTMLOptionsCollection.h"
51 #include "HTMLSelectElement.h"
52 #include "HTMLTextAreaElement.h"
53 #include "HitTestRequest.h"
54 #include "HitTestResult.h"
55 #include "LocalizedStrings.h"
56 #include "MathMLNames.h"
59 #include "ProgressTracker.h"
60 #include "RenderButton.h"
61 #include "RenderFieldset.h"
62 #include "RenderFileUploadControl.h"
63 #include "RenderHTMLCanvas.h"
64 #include "RenderImage.h"
65 #include "RenderInline.h"
66 #include "RenderLayer.h"
67 #include "RenderListBox.h"
68 #include "RenderListMarker.h"
69 #include "RenderMenuList.h"
70 #include "RenderText.h"
71 #include "RenderTextControl.h"
72 #include "RenderTextFragment.h"
73 #include "RenderTheme.h"
74 #include "RenderView.h"
75 #include "RenderWidget.h"
76 #include "RenderedPosition.h"
77 #include "SelectElement.h"
79 #include "TextIterator.h"
80 #include "htmlediting.h"
81 #include "visible_units.h"
82 #include <wtf/StdLibExtras.h>
83 #include <wtf/text/StringBuilder.h>
84 #include <wtf/unicode/CharacterNames.h>
90 using namespace HTMLNames;
92 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
93 : AccessibilityObject()
94 , m_renderer(renderer)
95 , m_ariaRole(UnknownRole)
96 , m_childrenDirty(false)
97 , m_roleForMSAA(UnknownRole)
99 m_role = determineAccessibilityRole();
102 m_renderer->setHasAXObject(true);
106 AccessibilityRenderObject::~AccessibilityRenderObject()
108 ASSERT(isDetached());
111 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
113 return adoptRef(new AccessibilityRenderObject(renderer));
116 void AccessibilityRenderObject::detach()
119 AccessibilityObject::detach();
123 m_renderer->setHasAXObject(false);
128 RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
130 if (!m_renderer || !m_renderer->isBoxModelObject())
132 return toRenderBoxModelObject(m_renderer);
135 static inline bool isInlineWithContinuation(RenderObject* object)
137 if (!object->isBoxModelObject())
140 RenderBoxModelObject* renderer = toRenderBoxModelObject(object);
141 if (!renderer->isRenderInline())
144 return toRenderInline(renderer)->continuation();
147 static inline RenderObject* firstChildInContinuation(RenderObject* renderer)
149 RenderObject* r = toRenderInline(renderer)->continuation();
152 if (r->isRenderBlock())
154 if (RenderObject* child = r->firstChild())
156 r = toRenderInline(r)->continuation();
162 static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
164 RenderObject* firstChild = renderer->firstChild();
166 if (!firstChild && isInlineWithContinuation(renderer))
167 firstChild = firstChildInContinuation(renderer);
173 static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer)
175 RenderObject* lastChild = renderer->lastChild();
177 RenderObject* cur = renderer;
179 if (!cur->isRenderInline() && !cur->isRenderBlock())
185 if (RenderObject* lc = cur->lastChild())
188 if (cur->isRenderInline()) {
189 cur = toRenderInline(cur)->inlineElementContinuation();
190 ASSERT_UNUSED(prev, cur || !toRenderInline(prev)->continuation());
192 cur = toRenderBlock(cur)->inlineElementContinuation();
198 AccessibilityObject* AccessibilityRenderObject::firstChild() const
203 RenderObject* firstChild = firstChildConsideringContinuation(m_renderer);
208 return axObjectCache()->getOrCreate(firstChild);
211 AccessibilityObject* AccessibilityRenderObject::lastChild() const
216 RenderObject* lastChild = lastChildConsideringContinuation(m_renderer);
221 return axObjectCache()->getOrCreate(lastChild);
224 static inline RenderInline* startOfContinuations(RenderObject* r)
226 if (r->isInlineElementContinuation())
227 return toRenderInline(r->node()->renderer());
229 // Blocks with a previous continuation always have a next continuation
230 if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation())
231 return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer());
236 static inline RenderObject* endOfContinuations(RenderObject* renderer)
238 RenderObject* prev = renderer;
239 RenderObject* cur = renderer;
241 if (!cur->isRenderInline() && !cur->isRenderBlock())
246 if (cur->isRenderInline()) {
247 cur = toRenderInline(cur)->inlineElementContinuation();
248 ASSERT(cur || !toRenderInline(prev)->continuation());
250 cur = toRenderBlock(cur)->inlineElementContinuation();
257 static inline RenderObject* childBeforeConsideringContinuations(RenderInline* r, RenderObject* child)
259 RenderBoxModelObject* curContainer = r;
260 RenderObject* cur = 0;
261 RenderObject* prev = 0;
263 while (curContainer) {
264 if (curContainer->isRenderInline()) {
265 cur = curContainer->firstChild();
270 cur = cur->nextSibling();
273 curContainer = toRenderInline(curContainer)->continuation();
274 } else if (curContainer->isRenderBlock()) {
275 if (curContainer == child)
279 curContainer = toRenderBlock(curContainer)->inlineElementContinuation();
283 ASSERT_NOT_REACHED();
288 static inline bool firstChildIsInlineContinuation(RenderObject* renderer)
290 return renderer->firstChild() && renderer->firstChild()->isInlineElementContinuation();
293 AccessibilityObject* AccessibilityRenderObject::previousSibling() const
298 RenderObject* previousSibling = 0;
300 // Case 1: The node is a block and is an inline's continuation. In that case, the inline's
301 // last child is our previous sibling (or further back in the continuation chain)
302 RenderInline* startOfConts;
303 if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
304 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer);
306 // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before
307 // the parent of the start, since everything in between will be linked up via the continuation.
308 else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(m_renderer)) {
309 RenderObject* firstParent = startOfContinuations(m_renderer->firstChild())->parent();
310 while (firstChildIsInlineContinuation(firstParent))
311 firstParent = startOfContinuations(firstParent->firstChild())->parent();
312 previousSibling = firstParent->previousSibling();
315 // Case 3: The node has an actual previous sibling
316 else if (RenderObject* ps = m_renderer->previousSibling())
317 previousSibling = ps;
319 // Case 4: This node has no previous siblings, but its parent is an inline,
320 // and is another node's inline continutation. Follow the continuation chain.
321 else if (m_renderer->parent()->isRenderInline() && (startOfConts = startOfContinuations(m_renderer->parent())))
322 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild());
324 if (!previousSibling)
327 return axObjectCache()->getOrCreate(previousSibling);
330 static inline bool lastChildHasContinuation(RenderObject* renderer)
332 return renderer->lastChild() && isInlineWithContinuation(renderer->lastChild());
335 AccessibilityObject* AccessibilityRenderObject::nextSibling() const
340 RenderObject* nextSibling = 0;
342 // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
344 RenderInline* inlineContinuation;
345 if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation()))
346 nextSibling = firstChildConsideringContinuation(inlineContinuation);
348 // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
349 // after the parent of the end, since everything in between will be linked up via the continuation.
350 else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) {
351 RenderObject* lastParent = endOfContinuations(m_renderer->lastChild())->parent();
352 while (lastChildHasContinuation(lastParent))
353 lastParent = endOfContinuations(lastParent->lastChild())->parent();
354 nextSibling = lastParent->nextSibling();
357 // Case 3: node has an actual next sibling
358 else if (RenderObject* ns = m_renderer->nextSibling())
361 // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
362 // of the continuation chain.
363 else if (isInlineWithContinuation(m_renderer))
364 nextSibling = endOfContinuations(m_renderer)->nextSibling();
366 // Case 5: node has no next sibling, and its parent is an inline with a continuation.
367 else if (isInlineWithContinuation(m_renderer->parent())) {
368 RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
370 // Case 5a: continuation is a block - in this case the block itself is the next sibling.
371 if (continuation->isRenderBlock())
372 nextSibling = continuation;
373 // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling
375 nextSibling = firstChildConsideringContinuation(continuation);
381 return axObjectCache()->getOrCreate(nextSibling);
384 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
387 if (renderer->isRenderInline() && !renderer->isReplaced())
388 return toRenderInline(renderer)->continuation();
389 if (renderer->isRenderBlock())
390 return toRenderBlock(renderer)->inlineElementContinuation();
394 RenderObject* AccessibilityRenderObject::renderParentObject() const
399 RenderObject* parent = m_renderer->parent();
401 // Case 1: node is a block and is an inline's continuation. Parent
402 // is the start of the continuation chain.
403 RenderObject* startOfConts = 0;
404 RenderObject* firstChild = 0;
405 if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
406 parent = startOfConts;
408 // Case 2: node's parent is an inline which is some node's continuation; parent is
409 // the earliest node in the continuation chain.
410 else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent)))
411 parent = startOfConts;
413 // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
414 else if (parent && (firstChild = parent->firstChild()) && firstChild->node()) {
415 // Get the node's renderer and follow that continuation chain until the first child is found
416 RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
417 while (nodeRenderFirstChild != firstChild) {
418 for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) {
419 if (contsTest == firstChild) {
420 parent = nodeRenderFirstChild->parent();
424 if (firstChild == parent->firstChild())
426 firstChild = parent->firstChild();
427 nodeRenderFirstChild = firstChild->node()->renderer();
434 AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
436 return axObjectCache()->get(renderParentObject());
439 AccessibilityObject* AccessibilityRenderObject::parentObject() const
444 if (ariaRoleAttribute() == MenuBarRole)
445 return axObjectCache()->getOrCreate(m_renderer->parent());
447 // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
448 if (ariaRoleAttribute() == MenuRole) {
449 AccessibilityObject* parent = menuButtonForMenu();
454 RenderObject* parentObj = renderParentObject();
456 return axObjectCache()->getOrCreate(parentObj);
458 // WebArea's parent should be the scroll view containing it.
460 return axObjectCache()->getOrCreate(m_renderer->frame()->view());
465 bool AccessibilityRenderObject::isWebArea() const
467 return roleValue() == WebAreaRole;
470 bool AccessibilityRenderObject::isImageButton() const
472 return isNativeImage() && roleValue() == ButtonRole;
475 bool AccessibilityRenderObject::isAnchor() const
477 return !isNativeImage() && isLink();
480 bool AccessibilityRenderObject::isNativeTextControl() const
482 return m_renderer->isTextControl();
485 bool AccessibilityRenderObject::isNativeImage() const
487 return m_renderer->isBoxModelObject() && toRenderBoxModelObject(m_renderer)->isImage();
490 bool AccessibilityRenderObject::isImage() const
492 return roleValue() == ImageRole;
495 bool AccessibilityRenderObject::isAttachment() const
497 RenderBoxModelObject* renderer = renderBoxModelObject();
500 // Widgets are the replaced elements that we represent to AX as attachments
501 bool isWidget = renderer->isWidget();
502 ASSERT(!isWidget || (renderer->isReplaced() && !isImage()));
503 return isWidget && ariaRoleAttribute() == UnknownRole;
506 bool AccessibilityRenderObject::isPasswordField() const
509 if (!m_renderer->node() || !m_renderer->node()->isHTMLElement())
511 if (ariaRoleAttribute() != UnknownRole)
514 HTMLInputElement* inputElement = m_renderer->node()->toInputElement();
518 return inputElement->isPasswordField();
521 bool AccessibilityRenderObject::isFileUploadButton() const
523 if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
524 HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
525 return input->isFileUpload();
531 bool AccessibilityRenderObject::isInputImage() const
533 Node* elementNode = node();
534 if (roleValue() == ButtonRole && elementNode && elementNode->hasTagName(inputTag)) {
535 HTMLInputElement* input = static_cast<HTMLInputElement*>(elementNode);
536 return input->isImageButton();
542 bool AccessibilityRenderObject::isProgressIndicator() const
544 return roleValue() == ProgressIndicatorRole;
547 bool AccessibilityRenderObject::isSlider() const
549 return roleValue() == SliderRole;
552 bool AccessibilityRenderObject::isMenuRelated() const
554 AccessibilityRole role = roleValue();
555 return role == MenuRole
556 || role == MenuBarRole
557 || role == MenuButtonRole
558 || role == MenuItemRole;
561 bool AccessibilityRenderObject::isMenu() const
563 return roleValue() == MenuRole;
566 bool AccessibilityRenderObject::isMenuBar() const
568 return roleValue() == MenuBarRole;
571 bool AccessibilityRenderObject::isMenuButton() const
573 return roleValue() == MenuButtonRole;
576 bool AccessibilityRenderObject::isMenuItem() const
578 return roleValue() == MenuItemRole;
581 bool AccessibilityRenderObject::isPressed() const
584 if (roleValue() != ButtonRole)
587 Node* node = m_renderer->node();
591 // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
592 if (ariaRoleAttribute() == ButtonRole) {
593 if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
598 return node->active();
601 bool AccessibilityRenderObject::isIndeterminate() const
604 if (!m_renderer->node())
607 HTMLInputElement* inputElement = m_renderer->node()->toInputElement();
611 return inputElement->isIndeterminate();
614 bool AccessibilityRenderObject::isNativeCheckboxOrRadio() const
616 Node* elementNode = node();
618 HTMLInputElement* input = elementNode->toInputElement();
620 return input->isCheckbox() || input->isRadioButton();
626 bool AccessibilityRenderObject::isChecked() const
629 if (!m_renderer->node())
632 // First test for native checkedness semantics
633 HTMLInputElement* inputElement = m_renderer->node()->toInputElement();
635 return inputElement->shouldAppearChecked();
637 // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
638 AccessibilityRole ariaRole = ariaRoleAttribute();
639 if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
640 if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
645 // Otherwise it's not checked
649 bool AccessibilityRenderObject::isHovered() const
652 return m_renderer->node() && m_renderer->node()->hovered();
655 bool AccessibilityRenderObject::isMultiSelectable() const
659 const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
660 if (equalIgnoringCase(ariaMultiSelectable, "true"))
662 if (equalIgnoringCase(ariaMultiSelectable, "false"))
665 if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isListBox())
667 return m_renderer->node() && static_cast<HTMLSelectElement*>(m_renderer->node())->multiple();
670 bool AccessibilityRenderObject::isReadOnly() const
675 Document* document = m_renderer->document();
679 HTMLElement* body = document->body();
680 if (body && body->isContentEditable())
683 return !document->rendererIsEditable();
686 if (m_renderer->isBoxModelObject()) {
687 RenderBoxModelObject* box = toRenderBoxModelObject(m_renderer);
688 if (box->isTextField())
689 return static_cast<HTMLInputElement*>(box->node())->readOnly();
690 if (box->isTextArea())
691 return static_cast<HTMLTextAreaElement*>(box->node())->readOnly();
694 return !m_renderer->node() || !m_renderer->node()->rendererIsEditable();
697 bool AccessibilityRenderObject::isOffScreen() const
700 LayoutRect contentRect = m_renderer->absoluteClippedOverflowRect();
701 FrameView* view = m_renderer->frame()->view();
702 FloatRect viewRect = view->visibleContentRect();
703 viewRect.intersect(contentRect);
704 return viewRect.isEmpty();
707 int AccessibilityRenderObject::headingLevel() const
709 // headings can be in block flow and non-block flow
710 Node* element = node();
714 if (ariaRoleAttribute() == HeadingRole)
715 return getAttribute(aria_levelAttr).toInt();
717 if (element->hasTagName(h1Tag))
720 if (element->hasTagName(h2Tag))
723 if (element->hasTagName(h3Tag))
726 if (element->hasTagName(h4Tag))
729 if (element->hasTagName(h5Tag))
732 if (element->hasTagName(h6Tag))
738 bool AccessibilityRenderObject::isHeading() const
740 return roleValue() == HeadingRole;
743 bool AccessibilityRenderObject::isLink() const
745 return roleValue() == WebCoreLinkRole;
748 bool AccessibilityRenderObject::isControl() const
753 Node* node = m_renderer->node();
754 return node && ((node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())
755 || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
758 bool AccessibilityRenderObject::isFieldset() const
760 RenderBoxModelObject* renderer = renderBoxModelObject();
763 return renderer->isFieldset();
766 bool AccessibilityRenderObject::isGroup() const
768 return roleValue() == GroupRole;
771 AccessibilityObject* AccessibilityRenderObject::selectedRadioButton()
776 // Find the child radio button that is selected (ie. the intValue == 1).
777 int count = m_children.size();
778 for (int i = 0; i < count; ++i) {
779 AccessibilityObject* object = m_children[i].get();
780 if (object->roleValue() == RadioButtonRole && object->checkboxOrRadioValue() == ButtonStateOn)
786 AccessibilityObject* AccessibilityRenderObject::selectedTabItem()
791 // Find the child tab item that is selected (ie. the intValue == 1).
792 AccessibilityObject::AccessibilityChildrenVector tabs;
795 int count = tabs.size();
796 for (int i = 0; i < count; ++i) {
797 AccessibilityObject* object = m_children[i].get();
798 if (object->isTabItem() && object->isChecked())
804 Element* AccessibilityRenderObject::anchorElement() const
809 AXObjectCache* cache = axObjectCache();
810 RenderObject* currRenderer;
812 // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
813 for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
814 if (currRenderer->isAnonymousBlock()) {
815 RenderObject* continuation = toRenderBlock(currRenderer)->continuation();
817 return cache->getOrCreate(continuation)->anchorElement();
821 // bail if none found
825 // search up the DOM tree for an anchor element
826 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
827 Node* node = currRenderer->node();
828 for ( ; node; node = node->parentNode()) {
829 if (node->hasTagName(aTag) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
830 return static_cast<Element*>(node);
836 Element* AccessibilityRenderObject::actionElement() const
841 Node* node = m_renderer->node();
843 if (node->hasTagName(inputTag)) {
844 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
845 if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
847 } else if (node->hasTagName(buttonTag))
848 return static_cast<Element*>(node);
851 if (isFileUploadButton())
852 return static_cast<Element*>(m_renderer->node());
854 if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
855 return static_cast<Element*>(m_renderer->node());
858 return static_cast<Element*>(m_renderer->node());
860 if (m_renderer->isBoxModelObject() && toRenderBoxModelObject(m_renderer)->isMenuList())
861 return static_cast<Element*>(m_renderer->node());
863 AccessibilityRole role = roleValue();
864 if (role == ButtonRole || role == PopUpButtonRole)
865 return static_cast<Element*>(m_renderer->node());
867 Element* elt = anchorElement();
869 elt = mouseButtonListener();
873 Element* AccessibilityRenderObject::mouseButtonListener() const
875 Node* node = m_renderer->node();
879 // check if our parent is a mouse button listener
880 while (node && !node->isElementNode())
881 node = node->parentNode();
886 // FIXME: Do the continuation search like anchorElement does
887 for (Element* element = static_cast<Element*>(node); element; element = element->parentElement()) {
888 if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
895 void AccessibilityRenderObject::alterSliderValue(bool increase)
897 if (roleValue() != SliderRole)
900 if (!getAttribute(stepAttr).isEmpty())
901 changeValueByStep(increase);
903 changeValueByPercent(increase ? 5 : -5);
906 void AccessibilityRenderObject::increment()
908 alterSliderValue(true);
911 void AccessibilityRenderObject::decrement()
913 alterSliderValue(false);
916 static Element* siblingWithAriaRole(String role, Node* node)
918 Node* sibling = node->parentNode()->firstChild();
920 if (sibling->isElementNode()) {
921 const AtomicString& siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr);
922 if (equalIgnoringCase(siblingAriaRole, role))
923 return static_cast<Element*>(sibling);
925 sibling = sibling->nextSibling();
931 Element* AccessibilityRenderObject::menuElementForMenuButton() const
933 if (ariaRoleAttribute() != MenuButtonRole)
936 return siblingWithAriaRole("menu", renderer()->node());
939 AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const
941 Element* menu = menuElementForMenuButton();
942 if (menu && menu->renderer())
943 return axObjectCache()->getOrCreate(menu->renderer());
947 Element* AccessibilityRenderObject::menuItemElementForMenu() const
949 if (ariaRoleAttribute() != MenuRole)
952 return siblingWithAriaRole("menuitem", renderer()->node());
955 AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const
957 Element* menuItem = menuItemElementForMenu();
959 if (menuItem && menuItem->renderer()) {
960 // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
961 AccessibilityObject* menuItemAX = axObjectCache()->getOrCreate(menuItem->renderer());
962 if (menuItemAX->isMenuButton())
968 String AccessibilityRenderObject::helpText() const
973 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
974 if (!ariaHelp.isEmpty())
977 for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
978 if (curr->node() && curr->node()->isHTMLElement()) {
979 const AtomicString& summary = static_cast<Element*>(curr->node())->getAttribute(summaryAttr);
980 if (!summary.isEmpty())
982 const AtomicString& title = static_cast<Element*>(curr->node())->getAttribute(titleAttr);
983 if (!title.isEmpty())
987 // Only take help text from an ancestor element if its a group or an unknown role. If help was
988 // added to those kinds of elements, it is likely it was meant for a child element.
989 AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
991 AccessibilityRole role = axObj->roleValue();
992 if (role != GroupRole && role != UnknownRole)
1000 unsigned AccessibilityRenderObject::hierarchicalLevel() const
1005 Node* node = m_renderer->node();
1006 if (!node || !node->isElementNode())
1008 Element* element = static_cast<Element*>(node);
1009 String ariaLevel = element->getAttribute(aria_levelAttr);
1010 if (!ariaLevel.isEmpty())
1011 return ariaLevel.toInt();
1013 // Only tree item will calculate its level through the DOM currently.
1014 if (roleValue() != TreeItemRole)
1017 // Hierarchy leveling starts at 0.
1018 // We measure tree hierarchy by the number of groups that the item is within.
1020 AccessibilityObject* parent = parentObject();
1022 AccessibilityRole parentRole = parent->roleValue();
1023 if (parentRole == GroupRole)
1025 else if (parentRole == TreeRole)
1028 parent = parent->parentObject();
1034 static TextIteratorBehavior textIteratorBehaviorForTextRange()
1036 TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
1039 // We need to emit replaced elements for GTK, and present
1040 // them with the 'object replacement character' (0xFFFC).
1041 behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
1047 String AccessibilityRenderObject::textUnderElement() const
1052 if (isFileUploadButton())
1053 return toRenderFileUploadControl(m_renderer)->buttonValue();
1055 Node* node = m_renderer->node();
1057 if (Frame* frame = node->document()->frame()) {
1058 // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
1059 if (frame->document() != node->document())
1062 return plainText(rangeOfContents(node).get(), textIteratorBehaviorForTextRange());
1066 // Sometimes text fragments don't have Node's associated with them (like when
1067 // CSS content is used to insert text).
1068 if (m_renderer->isText()) {
1069 RenderText* renderTextObject = toRenderText(m_renderer);
1070 if (renderTextObject->isTextFragment())
1071 return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
1074 // return the null string for anonymous text because it is non-trivial to get
1075 // the actual text and, so far, that is not needed
1079 Node* AccessibilityRenderObject::node() const
1081 return m_renderer ? m_renderer->node() : 0;
1084 AccessibilityButtonState AccessibilityRenderObject::checkboxOrRadioValue() const
1086 if (isNativeCheckboxOrRadio())
1087 return isChecked() ? ButtonStateOn : ButtonStateOff;
1089 return AccessibilityObject::checkboxOrRadioValue();
1092 String AccessibilityRenderObject::valueDescription() const
1094 // Only sliders and progress bars support value descriptions currently.
1095 if (!isProgressIndicator() && !isSlider())
1098 return getAttribute(aria_valuetextAttr).string();
1101 float AccessibilityRenderObject::stepValueForRange() const
1103 return getAttribute(stepAttr).toFloat();
1106 float AccessibilityRenderObject::valueForRange() const
1108 if (!isProgressIndicator() && !isSlider() && !isScrollbar())
1111 return getAttribute(aria_valuenowAttr).toFloat();
1114 float AccessibilityRenderObject::maxValueForRange() const
1116 if (!isProgressIndicator() && !isSlider())
1119 return getAttribute(aria_valuemaxAttr).toFloat();
1122 float AccessibilityRenderObject::minValueForRange() const
1124 if (!isProgressIndicator() && !isSlider())
1127 return getAttribute(aria_valueminAttr).toFloat();
1130 String AccessibilityRenderObject::stringValue() const
1132 if (!m_renderer || isPasswordField())
1135 RenderBoxModelObject* cssBox = renderBoxModelObject();
1137 if (ariaRoleAttribute() == StaticTextRole) {
1138 String staticText = text();
1139 if (!staticText.length())
1140 staticText = textUnderElement();
1144 if (m_renderer->isText())
1145 return textUnderElement();
1147 if (cssBox && cssBox->isMenuList()) {
1148 // RenderMenuList will go straight to the text() of its selected item.
1149 // This has to be overriden in the case where the selected item has an aria label
1150 SelectElement* selectNode = toSelectElement(static_cast<Element*>(m_renderer->node()));
1151 int selectedIndex = selectNode->selectedIndex();
1152 const Vector<Element*> listItems = selectNode->listItems();
1154 Element* selectedOption = 0;
1155 if (selectedIndex >= 0 && selectedIndex < (int)listItems.size())
1156 selectedOption = listItems[selectedIndex];
1157 if (selectedOption) {
1158 String overridenDescription = selectedOption->getAttribute(aria_labelAttr);
1159 if (!overridenDescription.isNull())
1160 return overridenDescription;
1163 return toRenderMenuList(m_renderer)->text();
1166 if (m_renderer->isListMarker())
1167 return toRenderListMarker(m_renderer)->text();
1169 if (cssBox && cssBox->isRenderButton())
1170 return toRenderButton(m_renderer)->text();
1173 // FIXME: Why would a renderer exist when the Document isn't attached to a frame?
1174 if (m_renderer->frame())
1177 ASSERT_NOT_REACHED();
1180 if (isTextControl())
1183 if (isFileUploadButton())
1184 return toRenderFileUploadControl(m_renderer)->fileTextValue();
1186 // FIXME: We might need to implement a value here for more types
1187 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
1188 // this would require subclassing or making accessibilityAttributeNames do something other than return a
1189 // single static array.
1193 // This function implements the ARIA accessible name as described by the Mozilla
1194 // ARIA Implementer's Guide.
1195 static String accessibleNameForNode(Node* node)
1197 if (node->isTextNode())
1198 return static_cast<Text*>(node)->data();
1200 if (node->hasTagName(inputTag))
1201 return static_cast<HTMLInputElement*>(node)->value();
1203 if (node->isHTMLElement()) {
1204 const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
1212 String AccessibilityRenderObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
1214 StringBuilder builder;
1215 unsigned size = elements.size();
1216 for (unsigned i = 0; i < size; ++i) {
1217 Element* idElement = elements[i];
1219 builder.append(accessibleNameForNode(idElement));
1220 for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement))
1221 builder.append(accessibleNameForNode(n));
1224 builder.append(' ');
1226 return builder.toString();
1229 void AccessibilityRenderObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
1231 Node* node = m_renderer->node();
1232 if (!node || !node->isElementNode())
1235 TreeScope* scope = node->treeScope();
1239 String idList = getAttribute(attribute).string();
1240 if (idList.isEmpty())
1243 idList.replace('\n', ' ');
1244 Vector<String> idVector;
1245 idList.split(' ', idVector);
1247 unsigned size = idVector.size();
1248 for (unsigned i = 0; i < size; ++i) {
1249 AtomicString idName(idVector[i]);
1250 Element* idElement = scope->getElementById(idName);
1252 elements.append(idElement);
1256 void AccessibilityRenderObject::ariaLabeledByElements(Vector<Element*>& elements) const
1258 elementsFromAttribute(elements, aria_labeledbyAttr);
1259 if (!elements.size())
1260 elementsFromAttribute(elements, aria_labelledbyAttr);
1263 String AccessibilityRenderObject::ariaLabeledByAttribute() const
1265 Vector<Element*> elements;
1266 ariaLabeledByElements(elements);
1268 return accessibilityDescriptionForElements(elements);
1271 static HTMLLabelElement* labelForElement(Element* element)
1273 RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
1274 unsigned len = list->length();
1275 for (unsigned i = 0; i < len; i++) {
1276 if (list->item(i)->hasTagName(labelTag)) {
1277 HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
1278 if (label->control() == element)
1286 HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
1291 // the control element should not be considered part of the label
1295 // find if this has a parent that is a label
1296 for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
1297 if (parentNode->hasTagName(labelTag))
1298 return static_cast<HTMLLabelElement*>(parentNode);
1304 String AccessibilityRenderObject::title() const
1306 AccessibilityRole ariaRole = ariaRoleAttribute();
1311 Node* node = m_renderer->node();
1315 const AtomicString& title = getAttribute(titleAttr);
1316 if (!title.isEmpty())
1319 bool isInputTag = node->hasTagName(inputTag);
1321 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1322 if (input->isTextButton())
1323 return input->value();
1326 if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) {
1327 HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
1328 if (label && !titleUIElement())
1329 return label->innerText();
1332 if (roleValue() == ButtonRole
1333 || ariaRole == ListBoxOptionRole
1334 || ariaRole == MenuItemRole
1335 || ariaRole == MenuButtonRole
1336 || ariaRole == RadioButtonRole
1337 || ariaRole == CheckBoxRole
1338 || ariaRole == TabRole
1339 || ariaRole == PopUpButtonRole
1342 return textUnderElement();
1347 String AccessibilityRenderObject::ariaDescribedByAttribute() const
1349 Vector<Element*> elements;
1350 elementsFromAttribute(elements, aria_describedbyAttr);
1352 return accessibilityDescriptionForElements(elements);
1355 String AccessibilityRenderObject::ariaAccessibilityDescription() const
1357 String ariaLabeledBy = ariaLabeledByAttribute();
1358 if (!ariaLabeledBy.isEmpty())
1359 return ariaLabeledBy;
1361 const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
1362 if (!ariaLabel.isEmpty())
1365 String ariaDescription = ariaDescribedByAttribute();
1366 if (!ariaDescription.isEmpty())
1367 return ariaDescription;
1372 String AccessibilityRenderObject::accessibilityDescription() const
1377 // Static text should not have a description, it should only have a stringValue.
1378 if (roleValue() == StaticTextRole)
1381 String ariaDescription = ariaAccessibilityDescription();
1382 if (!ariaDescription.isEmpty())
1383 return ariaDescription;
1385 Node* node = m_renderer->node();
1386 if (isImage() || isInputImage() || isNativeImage()) {
1387 if (node && node->isHTMLElement()) {
1388 const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
1396 if (node && node->isElementNode() && static_cast<Element*>(node)->isMathMLElement())
1397 return getAttribute(MathMLNames::alttextAttr);
1401 Document* document = m_renderer->document();
1403 // Check if the HTML element has an aria-label for the webpage.
1404 Element* documentElement = document->documentElement();
1405 if (documentElement) {
1406 const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
1407 if (!ariaLabel.isEmpty())
1411 Node* owner = document->ownerElement();
1413 if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1414 const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
1415 if (!title.isEmpty())
1417 return static_cast<HTMLFrameElementBase*>(owner)->getAttribute(nameAttr);
1419 if (owner->isHTMLElement())
1420 return toHTMLElement(owner)->getAttribute(nameAttr);
1422 owner = document->body();
1423 if (owner && owner->isHTMLElement())
1424 return toHTMLElement(owner)->getAttribute(nameAttr);
1430 LayoutRect AccessibilityRenderObject::boundingBoxRect() const
1432 RenderObject* obj = m_renderer;
1435 return LayoutRect();
1437 if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
1438 obj = obj->node()->renderer();
1440 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
1441 // For a web area, which will have the most elements of any element, absoluteQuads should be used.
1442 Vector<FloatQuad> quads;
1444 toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
1445 else if (isWebArea())
1446 obj->absoluteQuads(quads);
1448 obj->absoluteFocusRingQuads(quads);
1449 const size_t n = quads.size();
1451 return LayoutRect();
1454 for (size_t i = 0; i < n; ++i) {
1455 LayoutRect r = quads[i].enclosingBoundingBox();
1457 if (obj->style()->hasAppearance())
1458 obj->theme()->adjustRepaintRect(obj, r);
1463 // The size of the web area should be the content size, not the clipped size.
1464 if (isWebArea() && obj->frame()->view())
1465 result.setSize(obj->frame()->view()->contentsSize());
1470 LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const
1473 return LayoutRect();
1475 HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->node()));
1476 if (!label || !label->renderer())
1477 return boundingBoxRect();
1479 LayoutRect labelRect = axObjectCache()->getOrCreate(label->renderer())->elementRect();
1480 labelRect.unite(boundingBoxRect());
1484 LayoutRect AccessibilityRenderObject::elementRect() const
1486 // a checkbox or radio button should encompass its label
1487 if (isCheckboxOrRadio())
1488 return checkboxOrRadioRect();
1490 return boundingBoxRect();
1493 LayoutSize AccessibilityRenderObject::size() const
1495 LayoutRect rect = elementRect();
1499 LayoutPoint AccessibilityRenderObject::clickPoint() const
1501 // use the default position unless this is an editable web area, in which case we use the selection bounds.
1502 if (!isWebArea() || isReadOnly())
1503 return AccessibilityObject::clickPoint();
1505 VisibleSelection visSelection = selection();
1506 VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
1507 LayoutRect bounds = boundsForVisiblePositionRange(range);
1509 bounds.setLocation(m_renderer->document()->view()->screenToContents(bounds.location()));
1511 return LayoutPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
1514 AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
1516 Element* element = anchorElement();
1520 // Right now, we do not support ARIA links as internal link elements
1521 if (!element->hasTagName(aTag))
1523 HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element);
1525 KURL linkURL = anchor->href();
1526 String fragmentIdentifier = linkURL.fragmentIdentifier();
1527 if (fragmentIdentifier.isEmpty())
1530 // check if URL is the same as current URL
1531 KURL documentURL = m_renderer->document()->url();
1532 if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
1535 Node* linkedNode = m_renderer->document()->findAnchor(fragmentIdentifier);
1539 // The element we find may not be accessible, so find the first accessible object.
1540 return firstAccessibleObjectFromNode(linkedNode);
1543 ESpeak AccessibilityRenderObject::speakProperty() const
1546 return AccessibilityObject::speakProperty();
1548 return m_renderer->style()->speak();
1551 void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
1553 if (!m_renderer || roleValue() != RadioButtonRole)
1556 Node* node = m_renderer->node();
1557 if (!node || !node->hasTagName(inputTag))
1560 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1561 // if there's a form, then this is easy
1562 if (input->form()) {
1563 Vector<RefPtr<Node> > formElements;
1564 input->form()->getNamedElements(input->name(), formElements);
1566 unsigned len = formElements.size();
1567 for (unsigned i = 0; i < len; ++i) {
1568 Node* associateElement = formElements[i].get();
1569 if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement->renderer()))
1570 linkedUIElements.append(object);
1573 RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
1574 unsigned len = list->length();
1575 for (unsigned i = 0; i < len; ++i) {
1576 if (list->item(i)->hasTagName(inputTag)) {
1577 HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i));
1578 if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
1579 if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement->renderer()))
1580 linkedUIElements.append(object);
1587 // linked ui elements could be all the related radio buttons in a group
1588 // or an internal anchor connection
1589 void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1591 ariaFlowToElements(linkedUIElements);
1594 AccessibilityObject* linkedAXElement = internalLinkElement();
1595 if (linkedAXElement)
1596 linkedUIElements.append(linkedAXElement);
1599 if (roleValue() == RadioButtonRole)
1600 addRadioButtonGroupMembers(linkedUIElements);
1603 bool AccessibilityRenderObject::hasTextAlternative() const
1605 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
1606 // override the "label" element association.
1607 if (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).isEmpty())
1613 bool AccessibilityRenderObject::ariaHasPopup() const
1615 return elementAttributeValue(aria_haspopupAttr);
1618 bool AccessibilityRenderObject::supportsARIAFlowTo() const
1620 return !getAttribute(aria_flowtoAttr).isEmpty();
1623 void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
1625 Vector<Element*> elements;
1626 elementsFromAttribute(elements, aria_flowtoAttr);
1628 AXObjectCache* cache = axObjectCache();
1629 unsigned count = elements.size();
1630 for (unsigned k = 0; k < count; ++k) {
1631 Element* element = elements[k];
1632 AccessibilityObject* flowToElement = cache->getOrCreate(element->renderer());
1634 flowTo.append(flowToElement);
1639 bool AccessibilityRenderObject::supportsARIADropping() const
1641 const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
1642 return !dropEffect.isEmpty();
1645 bool AccessibilityRenderObject::supportsARIADragging() const
1647 const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
1648 return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
1651 bool AccessibilityRenderObject::isARIAGrabbed()
1653 return elementAttributeValue(aria_grabbedAttr);
1656 void AccessibilityRenderObject::determineARIADropEffects(Vector<String>& effects)
1658 const AtomicString& dropEffects = getAttribute(aria_dropeffectAttr);
1659 if (dropEffects.isEmpty()) {
1664 String dropEffectsString = dropEffects.string();
1665 dropEffectsString.replace('\n', ' ');
1666 dropEffectsString.split(' ', effects);
1669 bool AccessibilityRenderObject::exposesTitleUIElement() const
1674 // If this control is ignored (because it's invisible),
1675 // then the label needs to be exposed so it can be visible to accessibility.
1676 if (accessibilityIsIgnored())
1679 // checkbox or radio buttons don't expose the title ui element unless it has a title already
1680 if (isCheckboxOrRadio() && getAttribute(titleAttr).isEmpty())
1683 if (hasTextAlternative())
1689 AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1694 // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1696 return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend());
1698 if (!exposesTitleUIElement())
1701 Node* element = m_renderer->node();
1702 HTMLLabelElement* label = labelForElement(static_cast<Element*>(element));
1703 if (label && label->renderer())
1704 return axObjectCache()->getOrCreate(label->renderer());
1709 bool AccessibilityRenderObject::ariaIsHidden() const
1711 if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "true"))
1714 // aria-hidden hides this object and any children
1715 AccessibilityObject* object = parentObject();
1717 if (equalIgnoringCase(object->getAttribute(aria_hiddenAttr), "true"))
1719 object = object->parentObject();
1725 bool AccessibilityRenderObject::isDescendantOfBarrenParent() const
1727 for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
1728 if (!object->canHaveChildren())
1735 bool AccessibilityRenderObject::isAllowedChildOfTree() const
1737 // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1738 AccessibilityObject* axObj = parentObject();
1739 bool isInTree = false;
1741 if (axObj->isTree()) {
1745 axObj = axObj->parentObject();
1748 // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1750 AccessibilityRole role = roleValue();
1751 if (role != TreeItemRole && role != StaticTextRole)
1757 AccessibilityObjectInclusion AccessibilityRenderObject::accessibilityIsIgnoredBase() const
1759 // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
1761 // Ignore invisible elements.
1762 if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
1763 return IgnoreObject;
1765 // Anything marked as aria-hidden or a child of something aria-hidden must be hidden.
1767 return IgnoreObject;
1769 // Anything that is a presentational role must be hidden.
1770 if (isPresentationalChildOfAriaRole())
1771 return IgnoreObject;
1773 // Allow the platform to make a decision.
1774 AccessibilityObjectInclusion decision = accessibilityPlatformIncludesObject();
1775 if (decision == IncludeObject)
1776 return IncludeObject;
1777 if (decision == IgnoreObject)
1778 return IgnoreObject;
1780 return DefaultBehavior;
1783 bool AccessibilityRenderObject::accessibilityIsIgnored() const
1785 // Check first if any of the common reasons cause this element to be ignored.
1786 // Then process other use cases that need to be applied to all the various roles
1787 // that AccessibilityRenderObjects take on.
1788 AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
1789 if (decision == IncludeObject)
1791 if (decision == IgnoreObject)
1794 // If this element is within a parent that cannot have children, it should not be exposed.
1795 if (isDescendantOfBarrenParent())
1798 if (roleValue() == IgnoredRole)
1801 if (roleValue() == PresentationalRole || inheritsPresentationalRole())
1804 // An ARIA tree can only have tree items and static text as children.
1805 if (!isAllowedChildOfTree())
1808 // Allow the platform to decide if the attachment is ignored or not.
1810 return accessibilityIgnoreAttachment();
1812 // ignore popup menu items because AppKit does
1813 for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
1814 if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList())
1818 // find out if this element is inside of a label element.
1819 // if so, it may be ignored because it's the label for a checkbox or radio button
1820 AccessibilityObject* controlObject = correspondingControlForLabelElement();
1821 if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
1824 // NOTE: BRs always have text boxes now, so the text box check here can be removed
1825 if (m_renderer->isText()) {
1826 // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1827 if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole
1828 || parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole)
1830 RenderText* renderText = toRenderText(m_renderer);
1831 if (m_renderer->isBR() || !renderText->firstTextBox())
1834 // static text beneath TextControls is reported along with the text control text so it's ignored.
1835 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1836 if (parent->roleValue() == TextFieldRole)
1840 // text elements that are just empty whitespace should not be returned
1841 return renderText->text()->containsOnlyWhitespace();
1850 // all controls are accessible
1854 if (ariaRoleAttribute() != UnknownRole)
1857 // don't ignore labels, because they serve as TitleUIElements
1858 Node* node = m_renderer->node();
1859 if (node && node->hasTagName(labelTag))
1862 // Anything that is content editable should not be ignored.
1863 // However, one cannot just call node->rendererIsEditable() since that will ask if its parents
1864 // are also editable. Only the top level content editable region should be exposed.
1865 if (node && node->isElementNode()) {
1866 Element* element = static_cast<Element*>(node);
1867 const AtomicString& contentEditable = element->getAttribute(contenteditableAttr);
1868 if (equalIgnoringCase(contentEditable, "true"))
1872 // List items play an important role in defining the structure of lists. They should not be ignored.
1873 if (roleValue() == ListItemRole)
1876 // if this element has aria attributes on it, it should not be ignored.
1877 if (supportsARIAAttributes())
1880 if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
1881 return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
1883 // ignore images seemingly used as spacers
1885 if (node && node->isElementNode()) {
1886 Element* elt = static_cast<Element*>(node);
1887 const AtomicString& alt = elt->getAttribute(altAttr);
1888 // don't ignore an image that has an alt tag
1891 // informal standard is to ignore images with zero-length alt strings
1896 if (node && node->hasTagName(canvasTag)) {
1897 RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
1898 if (canvas->height() <= 1 || canvas->width() <= 1)
1903 if (isNativeImage()) {
1904 // check for one-dimensional image
1905 RenderImage* image = toRenderImage(m_renderer);
1906 if (image->height() <= 1 || image->width() <= 1)
1909 // check whether rendered image was stretched from one-dimensional file image
1910 if (image->cachedImage()) {
1911 LayoutSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor());
1912 return imageSize.height() <= 1 || imageSize.width() <= 1;
1918 if (isWebArea() || m_renderer->isListMarker())
1921 // Using the help text to decide an element's visibility is not as definitive
1922 // as previous checks, so this should remain as one of the last.
1923 if (!helpText().isEmpty())
1926 // By default, objects should be ignored so that the AX hierarchy is not
1927 // filled with unnecessary items.
1931 bool AccessibilityRenderObject::isLoaded() const
1933 return !m_renderer->document()->parser();
1936 double AccessibilityRenderObject::estimatedLoadingProgress() const
1944 Page* page = m_renderer->document()->page();
1948 return page->progress()->estimatedProgress();
1951 int AccessibilityRenderObject::layoutCount() const
1953 if (!m_renderer->isRenderView())
1955 return toRenderView(m_renderer)->frameView()->layoutCount();
1958 String AccessibilityRenderObject::text() const
1960 // If this is a user defined static text, use the accessible name computation.
1961 if (ariaRoleAttribute() == StaticTextRole)
1962 return ariaAccessibilityDescription();
1964 if (!isTextControl() || isPasswordField())
1967 Node* node = m_renderer->node();
1971 if (isNativeTextControl())
1972 return toRenderTextControl(m_renderer)->textFormControlElement()->value();
1974 if (!node->isElementNode())
1977 return static_cast<Element*>(node)->innerText();
1980 int AccessibilityRenderObject::textLength() const
1982 ASSERT(isTextControl());
1984 if (isPasswordField())
1985 return -1; // need to return something distinct from 0
1987 return text().length();
1990 PlainTextRange AccessibilityRenderObject::ariaSelectedTextRange() const
1992 Node* node = m_renderer->node();
1994 return PlainTextRange();
1996 ExceptionCode ec = 0;
1997 VisibleSelection visibleSelection = selection();
1998 RefPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
1999 if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, ec))
2000 return PlainTextRange();
2002 int start = indexForVisiblePosition(visibleSelection.start());
2003 int end = indexForVisiblePosition(visibleSelection.end());
2005 return PlainTextRange(start, end - start);
2008 String AccessibilityRenderObject::selectedText() const
2010 ASSERT(isTextControl());
2012 if (isPasswordField())
2013 return String(); // need to return something distinct from empty string
2015 if (isNativeTextControl()) {
2016 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
2017 return textControl->selectedText();
2020 if (ariaRoleAttribute() == UnknownRole)
2023 return doAXStringForRange(ariaSelectedTextRange());
2026 const AtomicString& AccessibilityRenderObject::accessKey() const
2028 Node* node = m_renderer->node();
2031 if (!node->isElementNode())
2033 return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
2036 VisibleSelection AccessibilityRenderObject::selection() const
2038 return m_renderer->frame()->selection()->selection();
2041 PlainTextRange AccessibilityRenderObject::selectedTextRange() const
2043 ASSERT(isTextControl());
2045 if (isPasswordField())
2046 return PlainTextRange();
2048 AccessibilityRole ariaRole = ariaRoleAttribute();
2049 if (isNativeTextControl() && ariaRole == UnknownRole) {
2050 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
2051 return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
2054 if (ariaRole == UnknownRole)
2055 return PlainTextRange();
2057 return ariaSelectedTextRange();
2060 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
2062 if (isNativeTextControl()) {
2063 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
2064 textControl->setSelectionRange(range.start, range.start + range.length);
2068 Document* document = m_renderer->document();
2071 Frame* frame = document->frame();
2074 Node* node = m_renderer->node();
2075 frame->selection()->setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
2076 Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
2079 KURL AccessibilityRenderObject::url() const
2081 if (isAnchor() && m_renderer->node()->hasTagName(aTag)) {
2082 if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement()))
2083 return anchor->href();
2087 return m_renderer->document()->url();
2089 if (isImage() && m_renderer->node() && m_renderer->node()->hasTagName(imgTag))
2090 return static_cast<HTMLImageElement*>(m_renderer->node())->src();
2093 return static_cast<HTMLInputElement*>(m_renderer->node())->src();
2098 bool AccessibilityRenderObject::isUnvisited() const
2100 // FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs?
2101 return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideUnvisitedLink;
2104 bool AccessibilityRenderObject::isVisited() const
2106 // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
2107 return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink;
2110 void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
2115 Node* node = m_renderer->node();
2116 if (!node || !node->isElementNode())
2119 Element* element = static_cast<Element*>(node);
2120 element->setAttribute(attributeName, (value) ? "true" : "false");
2123 bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
2128 return equalIgnoringCase(getAttribute(attributeName), "true");
2131 bool AccessibilityRenderObject::isRequired() const
2133 if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
2137 if (n && (n->isElementNode() && static_cast<Element*>(n)->isFormControlElement()))
2138 return static_cast<HTMLFormControlElement*>(n)->required();
2143 bool AccessibilityRenderObject::isSelected() const
2148 Node* node = m_renderer->node();
2152 const AtomicString& ariaSelected = getAttribute(aria_selectedAttr);
2153 if (equalIgnoringCase(ariaSelected, "true"))
2156 if (isTabItem() && isTabItemSelected())
2162 bool AccessibilityRenderObject::isTabItemSelected() const
2164 if (!isTabItem() || !m_renderer)
2167 Node* node = m_renderer->node();
2168 if (!node || !node->isElementNode())
2171 // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
2172 // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
2173 // focus inside of it.
2174 AccessibilityObject* focusedElement = focusedUIElement();
2175 if (!focusedElement)
2178 Vector<Element*> elements;
2179 elementsFromAttribute(elements, aria_controlsAttr);
2181 unsigned count = elements.size();
2182 for (unsigned k = 0; k < count; ++k) {
2183 Element* element = elements[k];
2184 AccessibilityObject* tabPanel = axObjectCache()->getOrCreate(element->renderer());
2186 // A tab item should only control tab panels.
2187 if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
2190 AccessibilityObject* checkFocusElement = focusedElement;
2191 // Check if the focused element is a descendant of the element controlled by the tab item.
2192 while (checkFocusElement) {
2193 if (tabPanel == checkFocusElement)
2195 checkFocusElement = checkFocusElement->parentObject();
2202 bool AccessibilityRenderObject::isFocused() const
2207 Document* document = m_renderer->document();
2211 Node* focusedNode = document->focusedNode();
2215 // A web area is represented by the Document node in the DOM tree, which isn't focusable.
2216 // Check instead if the frame's selection controller is focused
2217 if (focusedNode == m_renderer->node()
2218 || (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
2224 void AccessibilityRenderObject::setFocused(bool on)
2226 if (!canSetFocusAttribute())
2230 m_renderer->document()->setFocusedNode(0);
2232 if (m_renderer->node()->isElementNode())
2233 static_cast<Element*>(m_renderer->node())->focus();
2235 m_renderer->document()->setFocusedNode(m_renderer->node());
2239 void AccessibilityRenderObject::changeValueByStep(bool increase)
2241 float step = stepValueForRange();
2242 float value = valueForRange();
2244 value += increase ? step : -step;
2246 setValue(String::number(value));
2248 axObjectCache()->postNotification(m_renderer, AXObjectCache::AXValueChanged, true);
2251 void AccessibilityRenderObject::changeValueByPercent(float percentChange)
2253 float range = maxValueForRange() - minValueForRange();
2254 float value = valueForRange();
2256 value += range * (percentChange / 100);
2257 setValue(String::number(value));
2259 axObjectCache()->postNotification(m_renderer, AXObjectCache::AXValueChanged, true);
2262 void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
2264 // Setting selected only makes sense in trees and tables (and tree-tables).
2265 AccessibilityRole role = roleValue();
2266 if (role != TreeRole && role != TreeGridRole && role != TableRole)
2269 bool isMulti = isMultiSelectable();
2270 unsigned count = selectedRows.size();
2271 if (count > 1 && !isMulti)
2274 for (unsigned k = 0; k < count; ++k)
2275 selectedRows[k]->setSelected(true);
2278 void AccessibilityRenderObject::setValue(const String& string)
2280 if (!m_renderer || !m_renderer->node() || !m_renderer->node()->isElementNode())
2282 Element* element = static_cast<Element*>(m_renderer->node());
2284 if (!m_renderer->isBoxModelObject())
2286 RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer);
2288 // FIXME: Do we want to do anything here for ARIA textboxes?
2289 if (renderer->isTextField()) {
2290 // FIXME: This is not safe! Other elements could have a TextField renderer.
2291 static_cast<HTMLInputElement*>(element)->setValue(string);
2292 } else if (renderer->isTextArea()) {
2293 // FIXME: This is not safe! Other elements could have a TextArea renderer.
2294 static_cast<HTMLTextAreaElement*>(element)->setValue(string);
2298 void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
2300 Vector<Element*> elements;
2301 elementsFromAttribute(elements, aria_ownsAttr);
2303 unsigned count = elements.size();
2304 for (unsigned k = 0; k < count; ++k) {
2305 RenderObject* render = elements[k]->renderer();
2306 AccessibilityObject* obj = axObjectCache()->getOrCreate(render);
2308 axObjects.append(obj);
2312 bool AccessibilityRenderObject::supportsARIAOwns() const
2316 const AtomicString& ariaOwns = getAttribute(aria_ownsAttr);
2318 return !ariaOwns.isEmpty();
2321 bool AccessibilityRenderObject::isEnabled() const
2325 if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
2328 Node* node = m_renderer->node();
2329 if (!node || !node->isElementNode())
2332 return static_cast<Element*>(node)->isEnabledFormControl();
2335 RenderView* AccessibilityRenderObject::topRenderer() const
2337 Document* topDoc = topDocument();
2341 return topDoc->renderView();
2344 Document* AccessibilityRenderObject::document() const
2348 return m_renderer->document();
2351 Document* AccessibilityRenderObject::topDocument() const
2355 return document()->topDocument();
2358 FrameView* AccessibilityRenderObject::topDocumentFrameView() const
2360 RenderView* renderView = topRenderer();
2361 if (!renderView || !renderView->view())
2363 return renderView->view()->frameView();
2366 Widget* AccessibilityRenderObject::widget() const
2368 if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isWidget())
2370 return toRenderWidget(m_renderer)->widget();
2373 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
2375 // find an image that is using this map
2379 HTMLImageElement* imageElement = map->imageElement();
2383 return axObjectCache()->getOrCreate(imageElement->renderer());
2386 void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
2388 Document* document = m_renderer->document();
2389 RefPtr<HTMLCollection> coll = document->links();
2390 Node* curr = coll->firstItem();
2392 RenderObject* obj = curr->renderer();
2394 RefPtr<AccessibilityObject> axobj = document->axObjectCache()->getOrCreate(obj);
2396 if (!axobj->accessibilityIsIgnored() && axobj->isLink())
2397 result.append(axobj);
2399 Node* parent = curr->parentNode();
2400 if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) {
2401 AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
2402 areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr));
2403 areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent));
2404 areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent)));
2406 result.append(areaObject);
2409 curr = coll->nextItem();
2413 FrameView* AccessibilityRenderObject::documentFrameView() const
2415 if (!m_renderer || !m_renderer->document())
2418 // this is the RenderObject's Document's Frame's FrameView
2419 return m_renderer->document()->view();
2422 Widget* AccessibilityRenderObject::widgetForAttachmentView() const
2424 if (!isAttachment())
2426 return toRenderWidget(m_renderer)->widget();
2429 FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
2431 if (!m_renderer->isRenderView())
2433 // this is the RenderObject's Document's renderer's FrameView
2434 return m_renderer->view()->frameView();
2437 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
2438 // a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
2439 VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
2442 return VisiblePositionRange();
2444 // construct VisiblePositions for start and end
2445 Node* node = m_renderer->node();
2447 return VisiblePositionRange();
2449 VisiblePosition startPos = firstPositionInOrBeforeNode(node);
2450 VisiblePosition endPos = lastPositionInOrAfterNode(node);
2452 // the VisiblePositions are equal for nodes like buttons, so adjust for that
2453 // FIXME: Really? [button, 0] and [button, 1] are distinct (before and after the button)
2454 // I expect this code is only hit for things like empty divs? In which case I don't think
2455 // the behavior is correct here -- eseidel
2456 if (startPos == endPos) {
2457 endPos = endPos.next();
2458 if (endPos.isNull())
2462 return VisiblePositionRange(startPos, endPos);
2465 VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
2467 if (!lineCount || !m_renderer)
2468 return VisiblePositionRange();
2470 // iterate over the lines
2471 // FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the
2472 // last offset of the last line
2473 VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForPoint(IntPoint());
2474 VisiblePosition savedVisiblePos;
2475 while (--lineCount) {
2476 savedVisiblePos = visiblePos;
2477 visiblePos = nextLinePosition(visiblePos, 0);
2478 if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2479 return VisiblePositionRange();
2482 // make a caret selection for the marker position, then extend it to the line
2483 // NOTE: ignores results of sel.modify because it returns false when
2484 // starting at an empty line. The resulting selection in that case
2485 // will be a caret at visiblePos.
2486 FrameSelection selection;
2487 selection.setSelection(VisibleSelection(visiblePos));
2488 selection.modify(FrameSelection::AlterationExtend, DirectionRight, LineBoundary);
2490 return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
2493 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
2496 return VisiblePosition();
2498 if (isNativeTextControl())
2499 return toRenderTextControl(m_renderer)->visiblePositionForIndex(index);
2501 if (!allowsTextRanges() && !m_renderer->isText())
2502 return VisiblePosition();
2504 Node* node = m_renderer->node();
2506 return VisiblePosition();
2509 return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
2511 ExceptionCode ec = 0;
2512 RefPtr<Range> range = Range::create(m_renderer->document());
2513 range->selectNodeContents(node, ec);
2514 CharacterIterator it(range.get());
2515 it.advance(index - 1);
2516 return VisiblePosition(Position(it.range()->endContainer(ec), it.range()->endOffset(ec), Position::PositionIsOffsetInAnchor), UPSTREAM);
2519 int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
2521 if (isNativeTextControl()) {
2522 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
2523 return textControl->indexForVisiblePosition(pos);
2526 if (!isTextControl())
2529 Node* node = m_renderer->node();
2533 Position indexPosition = pos.deepEquivalent();
2534 if (indexPosition.isNull() || rootEditableElementForPosition(indexPosition) != node)
2537 ExceptionCode ec = 0;
2538 RefPtr<Range> range = Range::create(m_renderer->document());
2539 range->setStart(node, 0, ec);
2540 range->setEnd(indexPosition, ec);
2543 // We need to consider replaced elements for GTK, as they will be
2544 // presented with the 'object replacement character' (0xFFFC).
2545 return TextIterator::rangeLength(range.get(), true);
2547 return TextIterator::rangeLength(range.get());
2551 Element* AccessibilityRenderObject::rootEditableElementForPosition(const Position& position) const
2553 // Find the root editable or pseudo-editable (i.e. having an editable ARIA role) element.
2554 Element* result = 0;
2556 Element* rootEditableElement = position.rootEditableElement();
2558 for (Element* e = position.element(); e && e != rootEditableElement; e = e->parentElement()) {
2559 if (nodeIsTextControl(e))
2561 if (e->hasTagName(bodyTag))
2568 return rootEditableElement;
2571 bool AccessibilityRenderObject::nodeIsTextControl(const Node* node) const
2576 const AccessibilityObject* axObjectForNode = axObjectCache()->getOrCreate(node->renderer());
2577 if (!axObjectForNode)
2580 return axObjectForNode->isTextControl();
2583 LayoutRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
2585 if (visiblePositionRange.isNull())
2586 return LayoutRect();
2588 // Create a mutable VisiblePositionRange.
2589 VisiblePositionRange range(visiblePositionRange);
2590 LayoutRect rect1 = range.start.absoluteCaretBounds();
2591 LayoutRect rect2 = range.end.absoluteCaretBounds();
2593 // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds
2594 if (rect2.y() != rect1.y()) {
2595 VisiblePosition endOfFirstLine = endOfLine(range.start);
2596 if (range.start == endOfFirstLine) {
2597 range.start.setAffinity(DOWNSTREAM);
2598 rect1 = range.start.absoluteCaretBounds();
2600 if (range.end == endOfFirstLine) {
2601 range.end.setAffinity(UPSTREAM);
2602 rect2 = range.end.absoluteCaretBounds();
2606 LayoutRect ourrect = rect1;
2607 ourrect.unite(rect2);
2609 // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
2610 if (rect1.maxY() != rect2.maxY()) {
2611 RefPtr<Range> dataRange = makeRange(range.start, range.end);
2612 LayoutRect boundingBox = dataRange->boundingBox();
2613 String rangeString = plainText(dataRange.get());
2614 if (rangeString.length() > 1 && !boundingBox.isEmpty())
2615 ourrect = boundingBox;
2619 return m_renderer->document()->view()->contentsToScreen(ourrect);
2625 void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
2627 if (range.start.isNull() || range.end.isNull())
2630 // make selection and tell the document to use it. if it's zero length, then move to that position
2631 if (range.start == range.end)
2632 m_renderer->frame()->selection()->moveTo(range.start, UserTriggered);
2634 VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2635 m_renderer->frame()->selection()->setSelection(newSelection);
2639 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const LayoutPoint& point) const
2642 return VisiblePosition();
2644 // convert absolute point to view coordinates
2645 Document* topDoc = topDocument();
2646 if (!topDoc || !topDoc->renderer() || !topDoc->renderer()->view())
2647 return VisiblePosition();
2649 FrameView* frameView = topDoc->renderer()->view()->frameView();
2651 return VisiblePosition();
2653 RenderView* renderView = topRenderer();
2655 return VisiblePosition();
2657 Node* innerNode = 0;
2659 // locate the node containing the point
2660 LayoutPoint pointResult;
2662 LayoutPoint ourpoint;
2664 ourpoint = frameView->screenToContents(point);
2668 HitTestRequest request(HitTestRequest::ReadOnly |
2669 HitTestRequest::Active);
2670 HitTestResult result(ourpoint);
2671 renderView->layer()->hitTest(request, result);
2672 innerNode = result.innerNode();
2674 return VisiblePosition();
2676 RenderObject* renderer = innerNode->renderer();
2678 return VisiblePosition();
2680 pointResult = result.localPoint();
2682 // done if hit something other than a widget
2683 if (!renderer->isWidget())
2686 // descend into widget (FRAME, IFRAME, OBJECT...)
2687 Widget* widget = toRenderWidget(renderer)->widget();
2688 if (!widget || !widget->isFrameView())
2690 Frame* frame = static_cast<FrameView*>(widget)->frame();
2693 renderView = frame->document()->renderView();
2694 frameView = static_cast<FrameView*>(widget);
2697 return innerNode->renderer()->positionForPoint(pointResult);
2700 // NOTE: Consider providing this utility method as AX API
2701 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
2703 if (!isTextControl())
2704 return VisiblePosition();
2706 // lastIndexOK specifies whether the position after the last character is acceptable
2707 if (indexValue >= text().length()) {
2708 if (!lastIndexOK || indexValue > text().length())
2709 return VisiblePosition();
2711 VisiblePosition position = visiblePositionForIndex(indexValue);
2712 position.setAffinity(DOWNSTREAM);
2716 // NOTE: Consider providing this utility method as AX API
2717 int AccessibilityRenderObject::index(const VisiblePosition& position) const
2719 if (!isTextControl())
2722 if (renderObjectContainsPosition(m_renderer, position.deepEquivalent()))
2723 return indexForVisiblePosition(position);
2728 // Given a line number, the range of characters of the text associated with this accessibility
2729 // object that contains the line number.
2730 PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
2732 if (!isTextControl())
2733 return PlainTextRange();
2735 // iterate to the specified line
2736 VisiblePosition visiblePos = visiblePositionForIndex(0);
2737 VisiblePosition savedVisiblePos;
2738 for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) {
2739 savedVisiblePos = visiblePos;
2740 visiblePos = nextLinePosition(visiblePos, 0);
2741 if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2742 return PlainTextRange();
2745 // Get the end of the line based on the starting position.
2746 VisiblePosition endPosition = endOfLine(visiblePos);
2748 int index1 = indexForVisiblePosition(visiblePos);
2749 int index2 = indexForVisiblePosition(endPosition);
2751 // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
2752 if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
2755 // return nil rather than an zero-length range (to match AppKit)
2756 if (index1 == index2)
2757 return PlainTextRange();
2759 return PlainTextRange(index1, index2 - index1);
2762 // The composed character range in the text associated with this accessibility object that
2763 // is specified by the given index value. This parameterized attribute returns the complete
2764 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
2765 PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
2767 if (!isTextControl())
2768 return PlainTextRange();
2770 String elementText = text();
2771 if (!elementText.length() || index > elementText.length() - 1)
2772 return PlainTextRange();
2774 return PlainTextRange(index, 1);
2777 // A substring of the text associated with this accessibility object that is
2778 // specified by the given character range.
2779 String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
2781 if (isPasswordField())
2787 if (!isTextControl())
2790 String elementText = text();
2791 if (range.start + range.length > elementText.length())
2794 return elementText.substring(range.start, range.length);
2797 // The bounding rectangle of the text associated with this accessibility object that is
2798 // specified by the given range. This is the bounding rectangle a sighted user would see
2799 // on the display screen, in pixels.
2800 LayoutRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
2802 if (allowsTextRanges())
2803 return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
2804 return LayoutRect();
2807 AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
2812 HTMLMapElement* map = static_cast<HTMLMapElement*>(area->parentNode());
2813 AccessibilityObject* parent = accessibilityParentForImageMap(map);
2817 AccessibilityObject::AccessibilityChildrenVector children = parent->children();
2819 unsigned count = children.size();
2820 for (unsigned k = 0; k < count; ++k) {
2821 if (children[k]->elementRect().contains(point))
2822 return children[k].get();
2828 AccessibilityObject* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const
2830 if (!m_renderer || !m_renderer->hasLayer())
2833 RenderLayer* layer = toRenderBox(m_renderer)->layer();
2835 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
2836 HitTestResult hitTestResult = HitTestResult(point);
2837 layer->hitTest(request, hitTestResult);
2838 if (!hitTestResult.innerNode())
2840 Node* node = hitTestResult.innerNode()->shadowAncestorNode();
2842 if (node->hasTagName(areaTag))
2843 return accessibilityImageMapHitTest(static_cast<HTMLAreaElement*>(node), point);
2845 if (node->hasTagName(optionTag))
2846 node = static_cast<HTMLOptionElement*>(node)->ownerSelectElement();
2848 RenderObject* obj = node->renderer();
2852 AccessibilityObject* result = obj->document()->axObjectCache()->getOrCreate(obj);
2853 result->updateChildrenIfNecessary();
2855 // Allow the element to perform any hit-testing it might need to do to reach non-render children.
2856 result = result->elementAccessibilityHitTest(point);
2858 if (result && result->accessibilityIsIgnored()) {
2859 // If this element is the label of a control, a hit test should return the control.
2860 AccessibilityObject* controlObject = result->correspondingControlForLabelElement();
2861 if (controlObject && !controlObject->exposesTitleUIElement())
2862 return controlObject;
2864 result = result->parentObjectUnignored();
2870 bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
2872 switch (ariaRoleAttribute()) {
2878 case RadioGroupRole:
2880 case PopUpButtonRole:
2881 case ProgressIndicatorRole:
2886 /* FIXME: replace these with actual roles when they are added to AccessibilityRole
2899 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
2904 if (m_renderer->node() && !m_renderer->node()->isElementNode())
2906 Element* element = static_cast<Element*>(m_renderer->node());
2908 const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr);
2909 if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
2912 Element* target = element->treeScope()->getElementById(activeDescendantAttrStr);
2916 AccessibilityObject* obj = axObjectCache()->getOrCreate(target->renderer());
2917 if (obj && obj->isAccessibilityRenderObject())
2918 // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
2923 void AccessibilityRenderObject::handleAriaExpandedChanged()
2925 // Find if a parent of this object should handle aria-expanded changes.
2926 AccessibilityObject* containerParent = this->parentObject();
2927 while (containerParent) {
2928 bool foundParent = false;
2930 switch (containerParent->roleValue()) {
2945 containerParent = containerParent->parentObject();
2948 // Post that the row count changed.
2949 if (containerParent)
2950 axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged, true);
2952 // Post that the specific row either collapsed or expanded.
2953 if (roleValue() == RowRole || roleValue() == TreeItemRole)
2954 axObjectCache()->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed, true);
2957 void AccessibilityRenderObject::handleActiveDescendantChanged()
2959 Element* element = static_cast<Element*>(renderer()->node());
2962 Document* doc = renderer()->document();
2963 if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element)
2965 AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
2967 if (activedescendant && shouldFocusActiveDescendant())
2968 doc->axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
2971 AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
2973 HTMLLabelElement* labelElement = labelElementContainer();
2977 HTMLElement* correspondingControl = labelElement->control();
2978 if (!correspondingControl)
2981 return axObjectCache()->getOrCreate(correspondingControl->renderer());
2984 AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
2989 Node* node = m_renderer->node();
2990 if (node && node->isHTMLElement()) {
2991 HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
2993 return axObjectCache()->getOrCreate(label->renderer());
2999 bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject* renderer) const
3001 // AX clients will listen for AXValueChange on a text control.
3002 if (renderer->isTextControl())
3005 // AX clients will listen for AXSelectedChildrenChanged on listboxes.
3006 Node* node = renderer->node();
3007 if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox()))
3010 // Textboxes should send out notifications.
3011 if (nodeHasRole(node, "textbox"))
3017 AccessibilityObject* AccessibilityRenderObject::observableObject() const
3019 // Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
3020 for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
3021 if (renderObjectIsObservable(renderer))
3022 return axObjectCache()->getOrCreate(renderer);
3028 AccessibilityRole AccessibilityRenderObject::remapAriaRoleDueToParent(AccessibilityRole role) const
3030 // Some objects change their role based on their parent.
3031 // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop.
3032 // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
3033 // https://bugs.webkit.org/show_bug.cgi?id=65174
3035 if (role != ListBoxOptionRole && role != MenuItemRole)
3038 for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
3039 AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
3041 // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
3042 if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
3043 return MenuItemRole;
3044 // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
3045 if (role == MenuItemRole && parentAriaRole == GroupRole)
3046 return MenuButtonRole;
3048 // If the parent had a different role, then we don't need to continue searching up the chain.
3056 AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
3058 const AtomicString& ariaRole = getAttribute(roleAttr);
3059 if (ariaRole.isNull() || ariaRole.isEmpty())
3062 AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
3064 if (role == ButtonRole && ariaHasPopup())
3065 role = PopUpButtonRole;
3067 if (role == TextAreaRole && !ariaIsMultiline())
3068 role = TextFieldRole;
3070 role = remapAriaRoleDueToParent(role);
3078 AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const
3083 void AccessibilityRenderObject::updateAccessibilityRole()
3085 bool ignoredStatus = accessibilityIsIgnored();
3086 m_role = determineAccessibilityRole();
3088 // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
3089 if (ignoredStatus != accessibilityIsIgnored())
3093 AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
3098 m_ariaRole = determineAriaRoleAttribute();
3100 Node* node = m_renderer->node();
3101 AccessibilityRole ariaRole = ariaRoleAttribute();
3102 if (ariaRole != UnknownRole)
3105 RenderBoxModelObject* cssBox = renderBoxModelObject();
3107 if (node && node->isLink()) {
3108 if (cssBox && cssBox->isImage())
3109 return ImageMapRole;
3110 return WebCoreLinkRole;
3112 if (cssBox && cssBox->isListItem())
3113 return ListItemRole;
3114 if (m_renderer->isListMarker())
3115 return ListMarkerRole;
3116 if (node && node->hasTagName(buttonTag))
3118 if (m_renderer->isText())
3119 return StaticTextRole;
3120 if (cssBox && cssBox->isImage()) {
3121 if (node && node->hasTagName(inputTag))
3125 if (node && node->hasTagName(canvasTag))
3128 if (cssBox && cssBox->isRenderView())
3131 if (cssBox && cssBox->isTextField())
3132 return TextFieldRole;
3134 if (cssBox && cssBox->isTextArea())
3135 return TextAreaRole;
3137 if (node && node->hasTagName(inputTag)) {
3138 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
3139 if (input->isCheckbox())
3140 return CheckBoxRole;
3141 if (input->isRadioButton())
3142 return RadioButtonRole;
3143 if (input->isTextButton())
3147 if (node && node->hasTagName(buttonTag))
3150 if (isFileUploadButton())
3153 if (cssBox && cssBox->isMenuList())
3154 return PopUpButtonRole;
3160 if (node && node->hasTagName(MathMLNames::mathTag))
3161 return DocumentMathRole;
3164 if (node && node->hasTagName(ddTag))
3165 return DefinitionListDefinitionRole;
3167 if (node && node->hasTagName(dtTag))
3168 return DefinitionListTermRole;
3170 if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
3171 return AnnotationRole;
3174 // Gtk ATs expect all tables, data and layout, to be exposed as tables.
3175 if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag)))
3178 if (node && node->hasTagName(trTag))
3181 if (node && node->hasTagName(tableTag))
3185 // Table sections should be ignored.
3186 if (m_renderer->isTableSection())
3190 if (m_renderer->isHR())
3191 return SplitterRole;
3193 if (node && node->hasTagName(pTag))
3194 return ParagraphRole;
3196 if (node && node->hasTagName(labelTag))
3199 if (node && node->hasTagName(divTag))
3202 if (node && node->hasTagName(formTag))
3205 if (node && node->hasTagName(labelTag))
3209 if (m_renderer->isBlockFlow())
3212 // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group.
3213 if (supportsARIAAttributes())
3219 AccessibilityOrientation AccessibilityRenderObject::orientation() const
3221 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
3222 if (equalIgnoringCase(ariaOrientation, "horizontal"))
3223 return AccessibilityOrientationHorizontal;
3224 if (equalIgnoringCase(ariaOrientation, "vertical"))
3225 return AccessibilityOrientationVertical;
3227 return AccessibilityObject::orientation();
3230 bool AccessibilityRenderObject::inheritsPresentationalRole() const
3232 // ARIA spec says that when a parent object is presentational, and it has required child elements,
3233 // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
3234 // http://www.w3.org/WAI/PF/aria/complete#presentation
3235 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, listItemParents, ());
3237 HashSet<QualifiedName>* possibleParentTagNames = 0;
3238 switch (roleValue()) {
3240 case ListMarkerRole:
3241 if (listItemParents.isEmpty()) {
3242 listItemParents.add(ulTag);
3243 listItemParents.add(olTag);
3244 listItemParents.add(dlTag);
3246 possibleParentTagNames = &listItemParents;
3252 // Not all elements need to check for this, only ones that are required children.
3253 if (!possibleParentTagNames)
3256 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
3257 if (!parent->isAccessibilityRenderObject())
3260 Node* elementNode = static_cast<AccessibilityRenderObject*>(parent)->node();
3261 if (!elementNode || !elementNode->isElementNode())
3264 // If native tag of the parent element matches an acceptable name, then return
3265 // based on its presentational status.
3266 if (possibleParentTagNames->contains(static_cast<Element*>(elementNode)->tagQName()))
3267 return parent->roleValue() == PresentationalRole;
3273 bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
3275 // Walk the parent chain looking for a parent that has presentational children
3276 AccessibilityObject* parent;
3277 for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
3283 bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
3285 switch (m_ariaRole) {
3289 case ProgressIndicatorRole:
3290 // case SeparatorRole:
3297 bool AccessibilityRenderObject::canSetFocusAttribute() const
3300 Node* node = m_renderer->node();
3302 // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
3303 // do anything. For example, setFocusedNode() will do nothing if the current focused
3304 // node will not relinquish the focus.
3305 if (!node || !node->isElementNode())
3308 if (!static_cast<Element*>(node)->isEnabledFormControl())
3311 switch (roleValue()) {
3312 case WebCoreLinkRole:
3313 case ImageMapLinkRole:
3317 case PopUpButtonRole:
3319 case RadioButtonRole:
3323 return node->supportsFocus();
3327 bool AccessibilityRenderObject::canSetExpandedAttribute() const
3329 // An object can be expanded if it aria-expanded is true or false.
3330 const AtomicString& ariaExpanded = getAttribute(aria_expandedAttr);
3331 return equalIgnoringCase(ariaExpanded, "true") || equalIgnoringCase(ariaExpanded, "false");
3334 bool AccessibilityRenderObject::canSetValueAttribute() const
3336 if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true"))
3339 if (isProgressIndicator() || isSlider())
3342 if (isTextControl() && !isNativeTextControl())
3345 // Any node could be contenteditable, so isReadOnly should be relied upon
3346 // for this information for all elements.
3347 return !isReadOnly();
3350 bool AccessibilityRenderObject::canSetTextRangeAttributes() const
3352 return isTextControl();
3355 void AccessibilityRenderObject::contentChanged()
3357 // If this element supports ARIA live regions, then notify the AT of changes.
3358 AXObjectCache* cache = axObjectCache();
3359 for (RenderObject* renderParent = m_renderer; renderParent; renderParent = renderParent->parent()) {
3360 AccessibilityObject* parent = cache->get(renderParent);
3364 // If we find a parent that has ARIA live region on, send the notification and stop processing.
3365 // The spec does not talk about nested live regions.
3366 if (parent->supportsARIALiveRegion()) {
3367 axObjectCache()->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged, true);
3373 void AccessibilityRenderObject::childrenChanged()
3375 // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
3379 bool sentChildrenChanged = false;
3381 // Go up the accessibility parent chain, but only if the element already exists. This method is
3382 // called during render layouts, minimal work should be done.
3383 // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
3384 // At the same time, process ARIA live region changes.
3385 for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
3386 if (!parent->isAccessibilityRenderObject())
3389 AccessibilityRenderObject* axParent = toAccessibilityRenderObject(parent);
3391 // Send the children changed notification on the first accessibility render object ancestor.
3392 if (!sentChildrenChanged) {
3393 axObjectCache()->postNotification(axParent->renderer(), AXObjectCache::AXChildrenChanged, true);
3394 sentChildrenChanged = true;
3397 // Only do work if the children haven't been marked dirty. This has the effect of blocking
3398 // future live region change notifications until the AX tree has been accessed again. This
3399 // is a good performance win for all parties.
3400 if (!axParent->needsToUpdateChildren()) {
3401 axParent->setNeedsToUpdateChildren();
3403 // If this element supports ARIA live regions, then notify the AT of changes.
3404 if (axParent->supportsARIALiveRegion())
3405 axObjectCache()->postNotification(axParent->renderer(), AXObjectCache::AXLiveRegionChanged, true);
3410 bool AccessibilityRenderObject::canHaveChildren() const
3415 // Elements that should not have children
3416 switch (roleValue()) {
3419 case PopUpButtonRole:
3421 case RadioButtonRole:
3423 case StaticTextRole:
3424 case ListBoxOptionRole:
3432 void AccessibilityRenderObject::clearChildren()
3434 AccessibilityObject::clearChildren();
3435 m_childrenDirty = false;
3438 void AccessibilityRenderObject::updateChildrenIfNecessary()
3440 if (needsToUpdateChildren())
3443 AccessibilityObject::updateChildrenIfNecessary();
3446 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children()
3448 updateChildrenIfNecessary();
3453 void AccessibilityRenderObject::addChildren()
3455 // If the need to add more children in addition to existing children arises,
3456 // childrenChanged should have been called, leaving the object with no children.
3457 ASSERT(!m_haveChildren);
3459 // nothing to add if there is no RenderObject
3463 m_haveChildren = true;
3465 if (!canHaveChildren())
3468 // add all unignored acc children
3469 for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
3470 if (obj->accessibilityIsIgnored()) {
3472 // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
3473 // or its visibility has changed. In the latter case, this child may have a stale child cached.
3474 // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
3475 obj->clearChildren();
3477 AccessibilityChildrenVector children = obj->children();
3478 unsigned length = children.size();
3479 for (unsigned i = 0; i < length; ++i)
3480 m_children.append(children[i]);
3482 ASSERT(obj->parentObject() == this);
3483 m_children.append(obj);
3487 // FrameView's need to be inserted into the AX hierarchy when encountered.
3488 if (isAttachment()) {
3489 Widget* widget = widgetForAttachmentView();
3490 if (widget && widget->isFrameView())
3491 m_children.append(axObjectCache()->getOrCreate(widget));
3494 // for a RenderImage, add the <area> elements as individual accessibility objects
3495 RenderBoxModelObject* cssBox = renderBoxModelObject();
3496 if (cssBox && cssBox->isRenderImage()) {
3497 HTMLMapElement* map = toRenderImage(cssBox)->imageMap();
3499 for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) {
3501 // add an <area> element for this child if it has a link
3502 if (current->hasTagName(areaTag) && current->isLink()) {
3503 AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
3504 areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current));
3505 areaObject->setHTMLMapElement(map);
3506 areaObject->setParent(this);
3508 m_children.append(areaObject);
3515 const AtomicString& AccessibilityRenderObject::ariaLiveRegionStatus() const
3517 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive"));
3518 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite"));
3519 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off"));
3521 const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
3522 // These roles have implicit live region status.
3523 if (liveRegionStatus.isEmpty()) {
3524 switch (roleValue()) {
3525 case ApplicationAlertDialogRole:
3526 case ApplicationAlertRole:
3527 return liveRegionStatusAssertive;
3528 case ApplicationLogRole:
3529 case ApplicationStatusRole:
3530 return liveRegionStatusPolite;
3531 case ApplicationTimerRole:
3532 case ApplicationMarqueeRole:
3533 return liveRegionStatusOff;
3539 return liveRegionStatus;
3542 const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const
3544 DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text"));
3545 const AtomicString& relevant = getAttribute(aria_relevantAttr);
3547 // Default aria-relevant = "additions text".
3548 if (relevant.isEmpty())
3549 return defaultLiveRegionRelevant;
3554 bool AccessibilityRenderObject::ariaLiveRegionAtomic() const
3556 return elementAttributeValue(aria_atomicAttr);
3559 bool AccessibilityRenderObject::ariaLiveRegionBusy() const
3561 return elementAttributeValue(aria_busyAttr);
3564 void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
3566 // Get all the rows.
3567 AccessibilityChildrenVector allRows;
3568 ariaTreeRows(allRows);
3570 // Determine which rows are selected.
3571 bool isMulti = isMultiSelectable();
3573 // Prefer active descendant over aria-selected.
3574 AccessibilityObject* activeDesc = activeDescendant();
3575 if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
3576 result.append(activeDesc);
3581 unsigned count = allRows.size();
3582 for (unsigned k = 0; k < count; ++k) {
3583 if (allRows[k]->isSelected()) {
3584 result.append(allRows[k]);
3591 void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
3593 bool isMulti = isMultiSelectable();
3595 AccessibilityChildrenVector childObjects = children();
3596 unsigned childrenSize = childObjects.size();
3597 for (unsigned k = 0; k < childrenSize; ++k) {
3598 // Every child should have aria-role option, and if so, check for selected attribute/state.
3599 AccessibilityObject* child = childObjects[k].get();
3600 if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) {
3601 result.append(child);
3608 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
3610 ASSERT(result.isEmpty());
3612 // only listboxes should be asked for their selected children.
3613 AccessibilityRole role = roleValue();
3614 if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3615 ariaListboxSelectedChildren(result);
3616 else if (role == TreeRole || role == TreeGridRole || role == TableRole)
3617 ariaSelectedRows(result);
3620 void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
3625 unsigned length = m_children.size();
3626 for (unsigned i = 0; i < length; i++) {
3627 if (!m_children[i]->isOffScreen())
3628 result.append(m_children[i]);
3632 void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
3634 ASSERT(result.isEmpty());
3636 // only listboxes are asked for their visible children.
3637 if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3638 ASSERT_NOT_REACHED();
3641 return ariaListboxVisibleChildren(result);
3644 void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result)
3646 ASSERT(roleValue() == TabListRole);
3648 unsigned length = m_children.size();
3649 for (unsigned i = 0; i < length; ++i) {
3650 if (m_children[i]->isTabItem())
3651 result.append(m_children[i]);
3655 const String& AccessibilityRenderObject::actionVerb() const
3657 // FIXME: Need to add verbs for select elements.
3658 DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
3659 DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
3660 DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
3661 DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
3662 DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
3663 DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
3664 DEFINE_STATIC_LOCAL(const String, noAction, ());
3666 switch (roleValue()) {
3668 return buttonAction;
3671 return textFieldAction;
3672 case RadioButtonRole:
3673 return radioButtonAction;
3675 return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
3677 case WebCoreLinkRole:
3684 void AccessibilityRenderObject::setAccessibleName(const AtomicString& name)
3686 // Setting the accessible name can store the value in the DOM
3691 // For web areas, set the aria-label on the HTML element.
3693 domNode = m_renderer->document()->documentElement();
3695 domNode = m_renderer->node();
3697 if (domNode && domNode->isElementNode())
3698 static_cast<Element*>(domNode)->setAttribute(aria_labelAttr, name);
3701 static bool isLinkable(const AccessibilityRenderObject& object)
3703 if (!object.renderer())
3706 // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
3707 // Mozilla considers linkable.
3708 return object.isLink() || object.isImage() || object.renderer()->isText();
3711 String AccessibilityRenderObject::stringValueForMSAA() const
3713 if (isLinkable(*this)) {
3714 Element* anchor = anchorElement();
3715 if (anchor && anchor->hasTagName(aTag))
3716 return static_cast<HTMLAnchorElement*>(anchor)->href();
3719 return stringValue();
3722 bool AccessibilityRenderObject::isLinked() const
3724 if (!isLinkable(*this))
3727 Element* anchor = anchorElement();
3728 if (!anchor || !anchor->hasTagName(aTag))
3731 return !static_cast<HTMLAnchorElement*>(anchor)->href().isEmpty();
3734 bool AccessibilityRenderObject::hasBoldFont() const
3739 return m_renderer->style()->fontDescription().weight() >= FontWeightBold;
3742 bool AccessibilityRenderObject::hasItalicFont() const
3747 return m_renderer->style()->fontDescription().italic() == FontItalicOn;
3750 bool AccessibilityRenderObject::hasPlainText() const
3755 RenderStyle* style = m_renderer->style();
3757 return style->fontDescription().weight() == FontWeightNormal
3758 && style->fontDescription().italic() == FontItalicOff
3759 && style->textDecorationsInEffect() == TDNONE;
3762 bool AccessibilityRenderObject::hasSameFont(RenderObject* renderer) const
3764 if (!m_renderer || !renderer)
3767 return m_renderer->style()->fontDescription().family() == renderer->style()->fontDescription().family();
3770 bool AccessibilityRenderObject::hasSameFontColor(RenderObject* renderer) const
3772 if (!m_renderer || !renderer)
3775 return m_renderer->style()->visitedDependentColor(CSSPropertyColor) == renderer->style()->visitedDependentColor(CSSPropertyColor);
3778 bool AccessibilityRenderObject::hasSameStyle(RenderObject* renderer) const
3780 if (!m_renderer || !renderer)
3783 return m_renderer->style() == renderer->style();
3786 bool AccessibilityRenderObject::hasUnderline() const
3791 return m_renderer->style()->textDecorationsInEffect() & UNDERLINE;
3794 String AccessibilityRenderObject::nameForMSAA() const
3796 if (m_renderer && m_renderer->isText())
3797 return textUnderElement();
3802 static bool shouldReturnTagNameAsRoleForMSAA(const Element& element)
3804 // See "document structure",
3805 // https://wiki.mozilla.org/Accessibility/AT-Windows-API
3806 // FIXME: Add the other tag names that should be returned as the role.
3807 return element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
3808 || element.hasTagName(h3Tag) || element.hasTagName(h4Tag)
3809 || element.hasTagName(h5Tag) || element.hasTagName(h6Tag);
3812 String AccessibilityRenderObject::stringRoleForMSAA() const
3817 Node* node = m_renderer->node();
3818 if (!node || !node->isElementNode())
3821 Element* element = static_cast<Element*>(node);
3822 if (!shouldReturnTagNameAsRoleForMSAA(*element))
3825 return element->tagName();
3828 String AccessibilityRenderObject::positionalDescriptionForMSAA() const
3830 // See "positional descriptions",
3831 // https://wiki.mozilla.org/Accessibility/AT-Windows-API
3833 return "L" + String::number(headingLevel());
3835 // FIXME: Add positional descriptions for other elements.
3839 String AccessibilityRenderObject::descriptionForMSAA() const
3841 String description = positionalDescriptionForMSAA();
3842 if (!description.isEmpty())
3845 description = accessibilityDescription();
3846 if (!description.isEmpty()) {
3847 // From the Mozilla MSAA implementation:
3848 // "Signal to screen readers that this description is speakable and is not
3849 // a formatted positional information description. Don't localize the
3850 // 'Description: ' part of this string, it will be parsed out by assistive
3852 return "Description: " + description;
3858 static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer)
3863 if (renderer->isText())
3864 return EditableTextRole;
3866 if (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListItem())
3867 return ListItemRole;
3872 AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const
3874 if (m_roleForMSAA != UnknownRole)
3875 return m_roleForMSAA;
3877 m_roleForMSAA = msaaRoleForRenderer(m_renderer);
3879 if (m_roleForMSAA == UnknownRole)
3880 m_roleForMSAA = roleValue();
3882 return m_roleForMSAA;
3885 } // namespace WebCore