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 "core/accessibility/AXRenderObject.h"
32 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
33 #include "core/InputTypeNames.h"
34 #include "core/accessibility/AXImageMapLink.h"
35 #include "core/accessibility/AXInlineTextBox.h"
36 #include "core/accessibility/AXObjectCache.h"
37 #include "core/accessibility/AXSVGRoot.h"
38 #include "core/accessibility/AXSpinButton.h"
39 #include "core/accessibility/AXTable.h"
40 #include "core/dom/ElementTraversal.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/editing/FrameSelection.h"
43 #include "core/editing/RenderedPosition.h"
44 #include "core/editing/TextIterator.h"
45 #include "core/editing/VisibleUnits.h"
46 #include "core/editing/htmlediting.h"
47 #include "core/frame/LocalFrame.h"
48 #include "core/frame/Settings.h"
49 #include "core/html/HTMLImageElement.h"
50 #include "core/html/HTMLLabelElement.h"
51 #include "core/html/HTMLOptionElement.h"
52 #include "core/html/HTMLSelectElement.h"
53 #include "core/html/HTMLTextAreaElement.h"
54 #include "core/html/shadow/ShadowElementNames.h"
55 #include "core/loader/ProgressTracker.h"
56 #include "core/page/Page.h"
57 #include "core/rendering/HitTestResult.h"
58 #include "core/rendering/RenderFieldset.h"
59 #include "core/rendering/RenderFileUploadControl.h"
60 #include "core/rendering/RenderHTMLCanvas.h"
61 #include "core/rendering/RenderImage.h"
62 #include "core/rendering/RenderInline.h"
63 #include "core/rendering/RenderLayer.h"
64 #include "core/rendering/RenderListMarker.h"
65 #include "core/rendering/RenderMenuList.h"
66 #include "core/rendering/RenderTextControlSingleLine.h"
67 #include "core/rendering/RenderTextFragment.h"
68 #include "core/rendering/RenderView.h"
69 #include "core/rendering/RenderWidget.h"
70 #include "core/svg/SVGDocumentExtensions.h"
71 #include "core/svg/SVGSVGElement.h"
72 #include "core/svg/graphics/SVGImage.h"
73 #include "platform/text/PlatformLocale.h"
74 #include "wtf/StdLibExtras.h"
76 using blink::WebLocalizedString;
80 using namespace HTMLNames;
82 static inline RenderObject* firstChildInContinuation(const RenderInline& renderer)
84 RenderBoxModelObject* r = renderer.continuation();
87 if (r->isRenderBlock())
89 if (RenderObject* child = r->slowFirstChild())
91 r = toRenderInline(r)->continuation();
97 static inline bool isInlineWithContinuation(RenderObject* object)
99 if (!object->isBoxModelObject())
102 RenderBoxModelObject* renderer = toRenderBoxModelObject(object);
103 if (!renderer->isRenderInline())
106 return toRenderInline(renderer)->continuation();
109 static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
111 RenderObject* firstChild = renderer->slowFirstChild();
113 if (!firstChild && isInlineWithContinuation(renderer))
114 firstChild = firstChildInContinuation(toRenderInline(*renderer));
119 static inline RenderInline* startOfContinuations(RenderObject* r)
121 if (r->isInlineElementContinuation()) {
122 return toRenderInline(r->node()->renderer());
125 // Blocks with a previous continuation always have a next continuation
126 if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation())
127 return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer());
132 static inline RenderObject* endOfContinuations(RenderObject* renderer)
134 RenderObject* prev = renderer;
135 RenderObject* cur = renderer;
137 if (!cur->isRenderInline() && !cur->isRenderBlock())
142 if (cur->isRenderInline()) {
143 cur = toRenderInline(cur)->inlineElementContinuation();
144 ASSERT(cur || !toRenderInline(prev)->continuation());
146 cur = toRenderBlock(cur)->inlineElementContinuation();
153 static inline bool lastChildHasContinuation(RenderObject* renderer)
155 RenderObject* lastChild = renderer->slowLastChild();
156 return lastChild && isInlineWithContinuation(lastChild);
159 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
162 if (renderer->isRenderInline() && !renderer->isReplaced())
163 return toRenderInline(renderer)->continuation();
164 if (renderer->isRenderBlock())
165 return toRenderBlock(renderer)->inlineElementContinuation();
169 AXRenderObject::AXRenderObject(RenderObject* renderer)
170 : AXNodeObject(renderer->node())
171 , m_renderer(renderer)
172 , m_cachedElementRectDirty(true)
175 m_renderer->setHasAXObject(true);
179 PassRefPtr<AXRenderObject> AXRenderObject::create(RenderObject* renderer)
181 return adoptRef(new AXRenderObject(renderer));
184 AXRenderObject::~AXRenderObject()
186 ASSERT(isDetached());
189 LayoutRect AXRenderObject::elementRect() const
191 if (!m_explicitElementRect.isEmpty())
192 return m_explicitElementRect;
195 if (!m_renderer->isBox())
196 return computeElementRect();
198 for (const AXObject* obj = this; obj; obj = obj->parentObject()) {
199 if (obj->isAXRenderObject())
200 toAXRenderObject(obj)->checkCachedElementRect();
202 for (const AXObject* obj = this; obj; obj = obj->parentObject()) {
203 if (obj->isAXRenderObject())
204 toAXRenderObject(obj)->updateCachedElementRect();
207 return m_cachedElementRect;
210 void AXRenderObject::setRenderer(RenderObject* renderer)
212 m_renderer = renderer;
213 setNode(renderer->node());
216 RenderBoxModelObject* AXRenderObject::renderBoxModelObject() const
218 if (!m_renderer || !m_renderer->isBoxModelObject())
220 return toRenderBoxModelObject(m_renderer);
223 Document* AXRenderObject::topDocument() const
227 return &document()->topDocument();
230 bool AXRenderObject::shouldNotifyActiveDescendant() const
232 // We want to notify that the combo box has changed its active descendant,
233 // but we do not want to change the focus, because focus should remain with the combo box.
237 return shouldFocusActiveDescendant();
240 ScrollableArea* AXRenderObject::getScrollableAreaIfScrollable() const
242 // If the parent is a scroll view, then this object isn't really scrollable, the parent ScrollView should handle the scrolling.
243 if (parentObject() && parentObject()->isAXScrollView())
246 if (!m_renderer || !m_renderer->isBox())
249 RenderBox* box = toRenderBox(m_renderer);
250 if (!box->canBeScrolledAndHasScrollableArea())
253 return box->scrollableArea();
256 AccessibilityRole AXRenderObject::determineAccessibilityRole()
261 m_ariaRole = determineAriaRoleAttribute();
263 Node* node = m_renderer->node();
264 AccessibilityRole ariaRole = ariaRoleAttribute();
265 if (ariaRole != UnknownRole)
268 RenderBoxModelObject* cssBox = renderBoxModelObject();
270 if (node && node->isLink()) {
271 if (cssBox && cssBox->isImage())
275 if (cssBox && cssBox->isListItem())
277 if (m_renderer->isListMarker())
278 return ListMarkerRole;
279 if (isHTMLButtonElement(node))
280 return buttonRoleType();
281 if (isHTMLDetailsElement(node))
283 if (isHTMLSummaryElement(node)) {
284 if (node->parentElement() && isHTMLDetailsElement(node->parentElement()))
285 return DisclosureTriangleRole;
288 if (isHTMLLegendElement(node))
290 if (m_renderer->isText())
291 return StaticTextRole;
292 if (cssBox && cssBox->isImage()) {
293 if (isHTMLInputElement(node))
294 return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
300 // Note: if JavaScript is disabled, the renderer won't be a RenderHTMLCanvas.
301 if (isHTMLCanvasElement(node) && m_renderer->isCanvas())
304 if (cssBox && cssBox->isRenderView())
307 if (cssBox && cssBox->isTextField())
308 return TextFieldRole;
310 if (cssBox && cssBox->isTextArea())
313 if (isHTMLInputElement(node)) {
314 HTMLInputElement& input = toHTMLInputElement(*node);
315 const AtomicString& type = input.type();
316 if (type == InputTypeNames::checkbox)
318 if (type == InputTypeNames::radio)
319 return RadioButtonRole;
320 if (input.isTextButton())
321 return buttonRoleType();
322 if (type == InputTypeNames::color)
323 return ColorWellRole;
326 if (isFileUploadButton())
329 if (cssBox && cssBox->isMenuList())
330 return PopUpButtonRole;
335 if (m_renderer->isSVGImage())
337 if (m_renderer->isSVGRoot())
340 if (node && node->hasTagName(ddTag))
341 return DescriptionListDetailRole;
343 if (node && node->hasTagName(dtTag))
344 return DescriptionListTermRole;
346 if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
347 return AnnotationRole;
349 // Table sections should be ignored.
350 if (m_renderer->isTableSection())
353 if (m_renderer->isHR())
354 return HorizontalRuleRole;
356 if (isHTMLParagraphElement(node))
357 return ParagraphRole;
359 if (isHTMLLabelElement(node))
362 if (isHTMLDivElement(node))
365 if (isHTMLFormElement(node))
368 if (node && node->hasTagName(articleTag))
371 if (node && node->hasTagName(mainTag))
374 if (node && node->hasTagName(navTag))
375 return NavigationRole;
377 if (node && node->hasTagName(asideTag))
378 return ComplementaryRole;
380 if (node && node->hasTagName(sectionTag))
383 if (node && node->hasTagName(addressTag))
384 return ContentInfoRole;
386 if (node && node->hasTagName(dialogTag))
389 // The HTML element should not be exposed as an element. That's what the RenderView element does.
390 if (isHTMLHtmlElement(node))
393 if (node && node->hasTagName(iframeTag))
396 if (isEmbeddedObject())
397 return EmbeddedObjectRole;
399 if (node && node->hasTagName(figcaptionTag))
400 return FigcaptionRole;
402 if (node && node->hasTagName(figureTag))
405 // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section
406 // then it should not be exposed as whole page's banner/contentInfo
407 if (node && node->hasTagName(headerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
409 if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
412 if (isHTMLAnchorElement(node) && isClickable())
415 if (m_renderer->isRenderBlockFlow())
418 // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group.
419 if (supportsARIAAttributes())
425 void AXRenderObject::init()
427 AXNodeObject::init();
430 void AXRenderObject::detach()
432 AXNodeObject::detach();
434 detachRemoteSVGRoot();
438 m_renderer->setHasAXObject(false);
444 // Check object role or purpose.
447 bool AXRenderObject::isAttachment() const
449 RenderBoxModelObject* renderer = renderBoxModelObject();
452 // Widgets are the replaced elements that we represent to AX as attachments
453 bool isWidget = renderer->isWidget();
454 ASSERT(!isWidget || (renderer->isReplaced() && !isImage()));
458 bool AXRenderObject::isFileUploadButton() const
460 return m_renderer && isHTMLInputElement(m_renderer->node()) && toHTMLInputElement(*m_renderer->node()).type() == InputTypeNames::file;
463 static bool isLinkable(const AXObject& object)
465 if (!object.renderer())
468 // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
469 // Mozilla considers linkable.
470 return object.isLink() || object.isImage() || object.renderer()->isText();
473 bool AXRenderObject::isLinked() const
475 if (!isLinkable(*this))
478 Element* anchor = anchorElement();
479 if (!isHTMLAnchorElement(anchor))
482 return !toHTMLAnchorElement(*anchor).href().isEmpty();
485 bool AXRenderObject::isLoaded() const
487 return !m_renderer->document().parser();
490 bool AXRenderObject::isOffScreen() const
493 IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect());
494 FrameView* view = m_renderer->frame()->view();
495 IntRect viewRect = view->visibleContentRect();
496 viewRect.intersect(contentRect);
497 return viewRect.isEmpty();
500 bool AXRenderObject::isReadOnly() const
505 Document& document = m_renderer->document();
506 HTMLElement* body = document.body();
507 if (body && body->hasEditableStyle())
510 return !document.hasEditableStyle();
513 return AXNodeObject::isReadOnly();
516 bool AXRenderObject::isVisited() const
518 // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
519 return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink;
523 // Check object state.
526 bool AXRenderObject::isFocused() const
531 Document& document = m_renderer->document();
532 Element* focusedElement = document.focusedElement();
536 // A web area is represented by the Document node in the DOM tree, which isn't focusable.
537 // Check instead if the frame's selection controller is focused
538 if (focusedElement == m_renderer->node()
539 || (roleValue() == WebAreaRole && document.frame()->selection().isFocusedAndActive()))
545 bool AXRenderObject::isSelected() const
550 Node* node = m_renderer->node();
554 const AtomicString& ariaSelected = getAttribute(aria_selectedAttr);
555 if (equalIgnoringCase(ariaSelected, "true"))
558 if (isTabItem() && isTabItemSelected())
565 // Whether objects are ignored, i.e. not included in the tree.
568 AXObjectInclusion AXRenderObject::defaultObjectInclusion() const
570 // The following cases can apply to any element that's a subclass of AXRenderObject.
575 if (m_renderer->style()->visibility() != VISIBLE) {
576 // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
577 if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
578 return DefaultBehavior;
583 return AXObject::defaultObjectInclusion();
586 bool AXRenderObject::computeAccessibilityIsIgnored() const
589 ASSERT(m_initialized);
592 // Check first if any of the common reasons cause this element to be ignored.
593 // Then process other use cases that need to be applied to all the various roles
594 // that AXRenderObjects take on.
595 AXObjectInclusion decision = defaultObjectInclusion();
596 if (decision == IncludeObject)
598 if (decision == IgnoreObject)
601 // If this element is within a parent that cannot have children, it should not be exposed.
602 if (isDescendantOfBarrenParent())
605 if (roleValue() == IgnoredRole)
608 if ((roleValue() == NoneRole || roleValue() == PresentationalRole) || inheritsPresentationalRole())
611 // An ARIA tree can only have tree items and static text as children.
612 if (!isAllowedChildOfTree())
615 // TODO: we should refactor this - but right now this is necessary to make
616 // sure scroll areas stay in the tree.
620 // ignore popup menu items because AppKit does
621 for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
622 if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList())
626 // find out if this element is inside of a label element.
627 // if so, it may be ignored because it's the label for a checkbox or radio button
628 AXObject* controlObject = correspondingControlForLabelElement();
629 if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
632 // NOTE: BRs always have text boxes now, so the text box check here can be removed
633 if (m_renderer->isText()) {
634 // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
635 AXObject* parent = parentObjectUnignored();
636 if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole))
638 RenderText* renderText = toRenderText(m_renderer);
639 if (m_renderer->isBR() || !renderText->firstTextBox())
642 // Don't ignore static text in editable text controls.
643 for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
644 if (parent->roleValue() == TextFieldRole || parent->roleValue() == TextAreaRole)
648 // text elements that are just empty whitespace should not be returned
649 // FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'pre', or similar...
650 return renderText->text().impl()->containsOnlyWhitespace();
656 if (isLandmarkRelated())
662 // all controls are accessible
666 if (ariaRoleAttribute() != UnknownRole)
669 // don't ignore labels, because they serve as TitleUIElements
670 Node* node = m_renderer->node();
671 if (isHTMLLabelElement(node))
674 // Anything that is content editable should not be ignored.
675 // However, one cannot just call node->hasEditableStyle() since that will ask if its parents
676 // are also editable. Only the top level content editable region should be exposed.
677 if (hasContentEditableAttributeSet())
680 // List items play an important role in defining the structure of lists. They should not be ignored.
681 if (roleValue() == ListItemRole)
684 if (roleValue() == DialogRole)
687 if (roleValue() == FigcaptionRole)
690 if (roleValue() == FigureRole)
693 if (roleValue() == DetailsRole)
696 // if this element has aria attributes on it, it should not be ignored.
697 if (supportsARIAAttributes())
700 // <span> tags are inline tags and not meant to convey information if they have no other aria
701 // information on them. If we don't ignore them, they may emit signals expected to come from
702 // their parent. In addition, because included spans are GroupRole objects, and GroupRole
703 // objects are often containers with meaningful information, the inclusion of a span can have
704 // the side effect of causing the immediate parent accessible to be ignored. This is especially
705 // problematic for platforms which have distinct roles for textual block elements.
706 if (isHTMLSpanElement(node))
709 if (m_renderer->isRenderBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute())
710 return !toRenderBlockFlow(m_renderer)->firstLineBox() && !mouseButtonListener();
712 // ignore images seemingly used as spacers
715 // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important.
716 if (canSetFocusAttribute())
719 if (node && node->isElementNode()) {
720 Element* elt = toElement(node);
721 const AtomicString& alt = elt->getAttribute(altAttr);
722 // don't ignore an image that has an alt tag
723 if (!alt.string().containsOnlyWhitespace())
725 // informal standard is to ignore images with zero-length alt strings
730 if (isNativeImage() && m_renderer->isImage()) {
731 // check for one-dimensional image
732 RenderImage* image = toRenderImage(m_renderer);
733 if (image->height() <= 1 || image->width() <= 1)
736 // check whether rendered image was stretched from one-dimensional file image
737 if (image->cachedImage()) {
738 LayoutSize imageSize = image->cachedImage()->imageSizeForRenderer(m_renderer, image->view()->zoomFactor());
739 return imageSize.height() <= 1 || imageSize.width() <= 1;
746 if (canvasHasFallbackContent())
748 RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
749 if (canvas->height() <= 1 || canvas->width() <= 1)
751 // Otherwise fall through; use presence of help text, title, or description to decide.
754 if (isWebArea() || m_renderer->isListMarker())
757 // Using the help text, title or accessibility description (so we
758 // check if there's some kind of accessible name for the element)
759 // to decide an element's visibility is not as definitive as
760 // previous checks, so this should remain as one of the last.
762 // These checks are simplified in the interest of execution speed;
763 // for example, any element having an alt attribute will make it
764 // not ignored, rather than just images.
765 if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedbyAttr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).isEmpty())
768 // Don't ignore generic focusable elements like <div tabindex=0>
769 // unless they're completely empty, with no children.
770 if (isGenericFocusableElement() && node->hasChildren())
773 if (!ariaAccessibilityDescription().isEmpty())
776 // By default, objects should be ignored so that the AX hierarchy is not
777 // filled with unnecessary items.
782 // Properties of static elements.
785 const AtomicString& AXRenderObject::accessKey() const
787 Node* node = m_renderer->node();
790 if (!node->isElementNode())
792 return toElement(node)->getAttribute(accesskeyAttr);
795 AccessibilityOrientation AXRenderObject::orientation() const
797 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
798 if (equalIgnoringCase(ariaOrientation, "horizontal"))
799 return AccessibilityOrientationHorizontal;
800 if (equalIgnoringCase(ariaOrientation, "vertical"))
801 return AccessibilityOrientationVertical;
803 return AXObject::orientation();
806 String AXRenderObject::text() const
808 if (isPasswordField())
811 return AXNodeObject::text();
814 int AXRenderObject::textLength() const
816 if (!isTextControl())
819 if (isPasswordField())
820 return -1; // need to return something distinct from 0
822 return text().length();
825 KURL AXRenderObject::url() const
827 if (isAnchor() && isHTMLAnchorElement(m_renderer->node())) {
828 if (HTMLAnchorElement* anchor = toHTMLAnchorElement(anchorElement()))
829 return anchor->href();
833 return m_renderer->document().url();
835 if (isImage() && isHTMLImageElement(m_renderer->node()))
836 return toHTMLImageElement(*m_renderer->node()).src();
839 return toHTMLInputElement(m_renderer->node())->src();
845 // Properties of interactive elements.
848 static String queryString(WebLocalizedString::Name name)
850 return Locale::defaultLocale().queryString(name);
853 String AXRenderObject::actionVerb() const
855 switch (roleValue()) {
857 case ToggleButtonRole:
858 return queryString(WebLocalizedString::AXButtonActionVerb);
861 return queryString(WebLocalizedString::AXTextFieldActionVerb);
862 case RadioButtonRole:
863 return queryString(WebLocalizedString::AXRadioButtonActionVerb);
865 return queryString(isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb : WebLocalizedString::AXUncheckedCheckBoxActionVerb);
867 return queryString(WebLocalizedString::AXLinkActionVerb);
869 return emptyString();
873 String AXRenderObject::stringValue() const
878 if (isPasswordField())
881 RenderBoxModelObject* cssBox = renderBoxModelObject();
883 if (ariaRoleAttribute() == StaticTextRole) {
884 String staticText = text();
885 if (!staticText.length())
886 staticText = textUnderElement();
890 if (m_renderer->isText())
891 return textUnderElement();
893 if (cssBox && cssBox->isMenuList()) {
894 // RenderMenuList will go straight to the text() of its selected item.
895 // This has to be overridden in the case where the selected item has an ARIA label.
896 HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->node());
897 int selectedIndex = selectElement->selectedIndex();
898 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = selectElement->listItems();
899 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
900 const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
901 if (!overriddenDescription.isNull())
902 return overriddenDescription;
904 return toRenderMenuList(m_renderer)->text();
907 if (m_renderer->isListMarker())
908 return toRenderListMarker(m_renderer)->text();
911 // FIXME: Why would a renderer exist when the Document isn't attached to a frame?
912 if (m_renderer->frame())
915 ASSERT_NOT_REACHED();
921 if (m_renderer->isFileUploadControl())
922 return toRenderFileUploadControl(m_renderer)->fileTextValue();
924 // FIXME: We might need to implement a value here for more types
925 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
926 // this would require subclassing or making accessibilityAttributeNames do something other than return a
927 // single static array.
935 AXObject* AXRenderObject::activeDescendant() const
940 if (m_renderer->node() && !m_renderer->node()->isElementNode())
943 Element* element = toElement(m_renderer->node());
947 const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr);
948 if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
951 Element* target = element->treeScope().getElementById(activeDescendantAttrStr);
955 AXObject* obj = axObjectCache()->getOrCreate(target);
957 // An activedescendant is only useful if it has a renderer, because that's what's needed to post the notification.
958 if (obj && obj->isAXRenderObject())
964 void AXRenderObject::accessibilityChildrenFromAttribute(QualifiedName attr, AccessibilityChildrenVector& children) const
966 WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
967 elementsFromAttribute(elements, attr);
969 AXObjectCache* cache = axObjectCache();
970 unsigned count = elements.size();
971 for (unsigned k = 0; k < count; ++k) {
972 Element* element = elements[k];
973 AXObject* child = cache->getOrCreate(element);
975 children.append(child);
979 void AXRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
981 accessibilityChildrenFromAttribute(aria_flowtoAttr, flowTo);
984 void AXRenderObject::ariaControlsElements(AccessibilityChildrenVector& controls) const
986 accessibilityChildrenFromAttribute(aria_controlsAttr, controls);
989 void AXRenderObject::ariaDescribedbyElements(AccessibilityChildrenVector& describedby) const
991 accessibilityChildrenFromAttribute(aria_describedbyAttr, describedby);
994 void AXRenderObject::ariaLabelledbyElements(AccessibilityChildrenVector& labelledby) const
996 accessibilityChildrenFromAttribute(aria_labelledbyAttr, labelledby);
999 void AXRenderObject::ariaOwnsElements(AccessibilityChildrenVector& owns) const
1001 accessibilityChildrenFromAttribute(aria_ownsAttr, owns);
1004 bool AXRenderObject::ariaHasPopup() const
1006 return elementAttributeValue(aria_haspopupAttr);
1009 bool AXRenderObject::ariaRoleHasPresentationalChildren() const
1011 switch (m_ariaRole) {
1015 case ProgressIndicatorRole:
1016 case SpinButtonRole:
1017 // case SeparatorRole:
1024 bool AXRenderObject::isPresentationalChildOfAriaRole() const
1026 // Walk the parent chain looking for a parent that has presentational children
1028 for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
1034 bool AXRenderObject::shouldFocusActiveDescendant() const
1036 switch (ariaRoleAttribute()) {
1044 case PopUpButtonRole:
1045 case ProgressIndicatorRole:
1046 case RadioGroupRole:
1058 bool AXRenderObject::supportsARIADragging() const
1060 const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
1061 return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
1064 bool AXRenderObject::supportsARIADropping() const
1066 const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
1067 return !dropEffect.isEmpty();
1070 bool AXRenderObject::supportsARIAFlowTo() const
1072 return !getAttribute(aria_flowtoAttr).isEmpty();
1075 bool AXRenderObject::supportsARIAOwns() const
1079 const AtomicString& ariaOwns = getAttribute(aria_ownsAttr);
1081 return !ariaOwns.isEmpty();
1085 // ARIA live-region features.
1088 const AtomicString& AXRenderObject::ariaLiveRegionStatus() const
1090 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive", AtomicString::ConstructFromLiteral));
1091 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite", AtomicString::ConstructFromLiteral));
1092 DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off", AtomicString::ConstructFromLiteral));
1094 const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
1095 // These roles have implicit live region status.
1096 if (liveRegionStatus.isEmpty()) {
1097 switch (roleValue()) {
1098 case AlertDialogRole:
1100 return liveRegionStatusAssertive;
1103 return liveRegionStatusPolite;
1106 return liveRegionStatusOff;
1112 return liveRegionStatus;
1115 const AtomicString& AXRenderObject::ariaLiveRegionRelevant() const
1117 DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text", AtomicString::ConstructFromLiteral));
1118 const AtomicString& relevant = getAttribute(aria_relevantAttr);
1120 // Default aria-relevant = "additions text".
1121 if (relevant.isEmpty())
1122 return defaultLiveRegionRelevant;
1127 bool AXRenderObject::ariaLiveRegionAtomic() const
1129 return elementAttributeValue(aria_atomicAttr);
1132 bool AXRenderObject::ariaLiveRegionBusy() const
1134 return elementAttributeValue(aria_busyAttr);
1138 // Accessibility Text.
1141 String AXRenderObject::textUnderElement() const
1146 if (m_renderer->isFileUploadControl())
1147 return toRenderFileUploadControl(m_renderer)->buttonValue();
1149 if (m_renderer->isText())
1150 return toRenderText(m_renderer)->plainText();
1152 return AXNodeObject::textUnderElement();
1156 // Accessibility Text - (To be deprecated).
1159 String AXRenderObject::helpText() const
1164 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1165 if (!ariaHelp.isEmpty())
1168 String describedBy = ariaDescribedByAttribute();
1169 if (!describedBy.isEmpty())
1172 String description = accessibilityDescription();
1173 for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
1174 if (curr->node() && curr->node()->isHTMLElement()) {
1175 const AtomicString& summary = toElement(curr->node())->getAttribute(summaryAttr);
1176 if (!summary.isEmpty())
1179 // The title attribute should be used as help text unless it is already being used as descriptive text.
1180 const AtomicString& title = toElement(curr->node())->getAttribute(titleAttr);
1181 if (!title.isEmpty() && description != title)
1185 // Only take help text from an ancestor element if its a group or an unknown role. If help was
1186 // added to those kinds of elements, it is likely it was meant for a child element.
1187 AXObject* axObj = axObjectCache()->getOrCreate(curr);
1189 AccessibilityRole role = axObj->roleValue();
1190 if (role != GroupRole && role != UnknownRole)
1199 // Position and size.
1202 void AXRenderObject::checkCachedElementRect() const
1204 if (m_cachedElementRectDirty)
1210 if (!m_renderer->isBox())
1214 RenderBox* box = toRenderBox(m_renderer);
1215 if (box->frameRect() != m_cachedFrameRect)
1218 if (box->canBeScrolledAndHasScrollableArea()) {
1219 ScrollableArea* scrollableArea = box->scrollableArea();
1220 if (scrollableArea && scrollableArea->scrollPosition() != m_cachedScrollPosition)
1225 markCachedElementRectDirty();
1228 void AXRenderObject::updateCachedElementRect() const
1230 if (!m_cachedElementRectDirty)
1236 if (!m_renderer->isBox())
1239 RenderBox* box = toRenderBox(m_renderer);
1240 m_cachedFrameRect = box->frameRect();
1242 if (box->canBeScrolledAndHasScrollableArea()) {
1243 ScrollableArea* scrollableArea = box->scrollableArea();
1245 m_cachedScrollPosition = scrollableArea->scrollPosition();
1248 m_cachedElementRect = computeElementRect();
1249 m_cachedElementRectDirty = false;
1252 void AXRenderObject::markCachedElementRectDirty() const
1254 if (m_cachedElementRectDirty)
1257 // Marks children recursively, if this element changed.
1258 m_cachedElementRectDirty = true;
1259 for (AXObject* child = firstChild(); child; child = child->nextSibling())
1260 child->markCachedElementRectDirty();
1263 IntPoint AXRenderObject::clickPoint()
1265 // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong.
1266 if (isHeading() && children().size() == 1)
1267 return children()[0]->clickPoint();
1269 // use the default position unless this is an editable web area, in which case we use the selection bounds.
1270 if (!isWebArea() || isReadOnly())
1271 return AXObject::clickPoint();
1273 IntRect bounds = pixelSnappedIntRect(elementRect());
1274 return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
1281 AXObject* AXRenderObject::accessibilityHitTest(const IntPoint& point) const
1283 if (!m_renderer || !m_renderer->hasLayer())
1286 RenderLayer* layer = toRenderBox(m_renderer)->layer();
1288 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
1289 HitTestResult hitTestResult = HitTestResult(point);
1290 layer->hitTest(request, hitTestResult);
1291 if (!hitTestResult.innerNode())
1294 Node* node = hitTestResult.innerNode();
1295 if (node->isInShadowTree())
1296 node = node->shadowHost();
1298 if (isHTMLAreaElement(node))
1299 return accessibilityImageMapHitTest(toHTMLAreaElement(node), point);
1301 if (isHTMLOptionElement(node))
1302 node = toHTMLOptionElement(*node).ownerSelectElement();
1304 RenderObject* obj = node->renderer();
1308 AXObject* result = obj->document().axObjectCache()->getOrCreate(obj);
1309 result->updateChildrenIfNecessary();
1311 // Allow the element to perform any hit-testing it might need to do to reach non-render children.
1312 result = result->elementAccessibilityHitTest(point);
1314 if (result && result->accessibilityIsIgnored()) {
1315 // If this element is the label of a control, a hit test should return the control.
1316 if (result->isAXRenderObject()) {
1317 AXObject* controlObject = toAXRenderObject(result)->correspondingControlForLabelElement();
1318 if (controlObject && !controlObject->exposesTitleUIElement())
1319 return controlObject;
1322 result = result->parentObjectUnignored();
1328 AXObject* AXRenderObject::elementAccessibilityHitTest(const IntPoint& point) const
1331 return remoteSVGElementHitTest(point);
1333 return AXObject::elementAccessibilityHitTest(point);
1337 // High-level accessibility tree access.
1340 AXObject* AXRenderObject::parentObject() const
1345 if (ariaRoleAttribute() == MenuBarRole)
1346 return axObjectCache()->getOrCreate(m_renderer->parent());
1348 // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
1349 if (ariaRoleAttribute() == MenuRole) {
1350 AXObject* parent = menuButtonForMenu();
1355 RenderObject* parentObj = renderParentObject();
1357 return axObjectCache()->getOrCreate(parentObj);
1359 // WebArea's parent should be the scroll view containing it.
1361 return axObjectCache()->getOrCreate(m_renderer->frame()->view());
1366 AXObject* AXRenderObject::parentObjectIfExists() const
1368 // WebArea's parent should be the scroll view containing it.
1370 return axObjectCache()->get(m_renderer->frame()->view());
1372 return axObjectCache()->get(renderParentObject());
1376 // Low-level accessibility tree exploration, only for use within the accessibility module.
1379 AXObject* AXRenderObject::firstChild() const
1384 RenderObject* firstChild = firstChildConsideringContinuation(m_renderer);
1389 return axObjectCache()->getOrCreate(firstChild);
1392 AXObject* AXRenderObject::nextSibling() const
1397 RenderObject* nextSibling = 0;
1399 RenderInline* inlineContinuation = m_renderer->isRenderBlock() ? toRenderBlock(m_renderer)->inlineElementContinuation() : 0;
1400 if (inlineContinuation) {
1401 // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's first child.
1402 nextSibling = firstChildConsideringContinuation(inlineContinuation);
1403 } else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) {
1404 // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
1405 // after the parent of the end, since everything in between will be linked up via the continuation.
1406 RenderObject* lastParent = endOfContinuations(toRenderBlock(m_renderer)->lastChild())->parent();
1407 while (lastChildHasContinuation(lastParent))
1408 lastParent = endOfContinuations(lastParent->slowLastChild())->parent();
1409 nextSibling = lastParent->nextSibling();
1410 } else if (RenderObject* ns = m_renderer->nextSibling()) {
1411 // Case 3: node has an actual next sibling
1413 } else if (isInlineWithContinuation(m_renderer)) {
1414 // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
1415 // of the continuation chain.
1416 nextSibling = endOfContinuations(m_renderer)->nextSibling();
1417 } else if (isInlineWithContinuation(m_renderer->parent())) {
1418 // Case 5: node has no next sibling, and its parent is an inline with a continuation.
1419 RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
1421 if (continuation->isRenderBlock()) {
1422 // Case 5a: continuation is a block - in this case the block itself is the next sibling.
1423 nextSibling = continuation;
1425 // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling.
1426 nextSibling = firstChildConsideringContinuation(continuation);
1433 return axObjectCache()->getOrCreate(nextSibling);
1436 void AXRenderObject::addChildren()
1438 // If the need to add more children in addition to existing children arises,
1439 // childrenChanged should have been called, leaving the object with no children.
1440 ASSERT(!m_haveChildren);
1442 m_haveChildren = true;
1444 if (!canHaveChildren())
1447 for (RefPtr<AXObject> obj = firstChild(); obj; obj = obj->nextSibling())
1448 addChild(obj.get());
1450 addHiddenChildren();
1451 addAttachmentChildren();
1453 addImageMapChildren();
1454 addTextFieldChildren();
1455 addCanvasChildren();
1456 addRemoteSVGChildren();
1457 addInlineTextBoxChildren();
1460 bool AXRenderObject::canHaveChildren() const
1465 return AXNodeObject::canHaveChildren();
1468 void AXRenderObject::updateChildrenIfNecessary()
1470 if (needsToUpdateChildren())
1473 AXObject::updateChildrenIfNecessary();
1476 void AXRenderObject::clearChildren()
1478 AXObject::clearChildren();
1479 m_childrenDirty = false;
1482 AXObject* AXRenderObject::observableObject() const
1484 // Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
1485 for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
1486 if (renderObjectIsObservable(renderer))
1487 return axObjectCache()->getOrCreate(renderer);
1494 // Properties of the object's owning document or page.
1497 double AXRenderObject::estimatedLoadingProgress() const
1505 if (LocalFrame* frame = m_renderer->document().frame())
1506 return frame->loader().progress().estimatedProgress();
1511 // DOM and Render tree access.
1514 Node* AXRenderObject::node() const
1516 return m_renderer ? m_renderer->node() : 0;
1519 Document* AXRenderObject::document() const
1523 return &m_renderer->document();
1526 FrameView* AXRenderObject::documentFrameView() const
1531 // this is the RenderObject's Document's LocalFrame's FrameView
1532 return m_renderer->document().view();
1535 Element* AXRenderObject::anchorElement() const
1540 AXObjectCache* cache = axObjectCache();
1541 RenderObject* currRenderer;
1543 // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
1544 for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
1545 if (currRenderer->isAnonymousBlock()) {
1546 RenderObject* continuation = toRenderBlock(currRenderer)->continuation();
1548 return cache->getOrCreate(continuation)->anchorElement();
1552 // bail if none found
1556 // search up the DOM tree for an anchor element
1557 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
1558 Node* node = currRenderer->node();
1559 for ( ; node; node = node->parentNode()) {
1560 if (isHTMLAnchorElement(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
1561 return toElement(node);
1567 Widget* AXRenderObject::widgetForAttachmentView() const
1569 if (!isAttachment())
1571 return toRenderWidget(m_renderer)->widget();
1578 AXObject::PlainTextRange AXRenderObject::selectedTextRange() const
1580 if (!isTextControl())
1581 return PlainTextRange();
1583 if (isPasswordField())
1584 return PlainTextRange();
1586 AccessibilityRole ariaRole = ariaRoleAttribute();
1587 if (isNativeTextControl() && ariaRole == UnknownRole && m_renderer->isTextControl()) {
1588 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1589 return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1592 if (ariaRole == UnknownRole)
1593 return PlainTextRange();
1595 return ariaSelectedTextRange();
1598 VisibleSelection AXRenderObject::selection() const
1600 return m_renderer->frame()->selection().selection();
1604 // Modify or take an action on an object.
1607 void AXRenderObject::setSelectedTextRange(const PlainTextRange& range)
1609 if (isNativeTextControl() && m_renderer->isTextControl()) {
1610 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1611 textControl->setSelectionRange(range.start, range.start + range.length);
1615 Document& document = m_renderer->document();
1616 LocalFrame* frame = document.frame();
1619 Node* node = m_renderer->node();
1620 frame->selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
1621 Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
1624 void AXRenderObject::setValue(const String& string)
1626 if (!node() || !node()->isElementNode())
1628 if (!m_renderer || !m_renderer->isBoxModelObject())
1631 RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer);
1632 if (renderer->isTextField() && isHTMLInputElement(*node()))
1633 toHTMLInputElement(*node()).setValue(string);
1634 else if (renderer->isTextArea() && isHTMLTextAreaElement(*node()))
1635 toHTMLTextAreaElement(*node()).setValue(string);
1638 // FIXME: This function should use an IntSize to avoid the conversion below.
1639 void AXRenderObject::scrollTo(const IntPoint& point) const
1641 if (!m_renderer || !m_renderer->isBox())
1644 RenderBox* box = toRenderBox(m_renderer);
1645 if (!box->canBeScrolledAndHasScrollableArea())
1648 box->scrollToOffset(IntSize(point.x(), point.y()));
1652 // Notifications that this object may have changed.
1655 void AXRenderObject::handleActiveDescendantChanged()
1657 Element* element = toElement(renderer()->node());
1660 Document& doc = renderer()->document();
1661 if (!doc.frame()->selection().isFocusedAndActive() || doc.focusedElement() != element)
1663 AXRenderObject* activedescendant = toAXRenderObject(activeDescendant());
1665 if (activedescendant && shouldNotifyActiveDescendant())
1666 doc.axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
1669 void AXRenderObject::handleAriaExpandedChanged()
1671 // Find if a parent of this object should handle aria-expanded changes.
1672 AXObject* containerParent = this->parentObject();
1673 while (containerParent) {
1674 bool foundParent = false;
1676 switch (containerParent->roleValue()) {
1691 containerParent = containerParent->parentObject();
1694 // Post that the row count changed.
1695 if (containerParent)
1696 axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged, true);
1698 // Post that the specific row either collapsed or expanded.
1699 if (roleValue() == RowRole || roleValue() == TreeItemRole)
1700 axObjectCache()->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed, true);
1703 void AXRenderObject::textChanged()
1708 Settings* settings = document()->settings();
1709 if (settings && settings->inlineTextBoxAccessibilityEnabled() && roleValue() == StaticTextRole)
1712 // Do this last - AXNodeObject::textChanged posts live region announcements,
1713 // and we should update the inline text boxes first.
1714 AXNodeObject::textChanged();
1718 // Text metrics. Most of these should be deprecated, needs major cleanup.
1721 // NOTE: Consider providing this utility method as AX API
1722 int AXRenderObject::index(const VisiblePosition& position) const
1724 if (position.isNull() || !isTextControl())
1727 if (renderObjectContainsPosition(m_renderer, position.deepEquivalent()))
1728 return indexForVisiblePosition(position);
1733 VisiblePosition AXRenderObject::visiblePositionForIndex(int index) const
1736 return VisiblePosition();
1738 if (isNativeTextControl() && m_renderer->isTextControl())
1739 return toRenderTextControl(m_renderer)->textFormControlElement()->visiblePositionForIndex(index);
1741 if (!allowsTextRanges() && !m_renderer->isText())
1742 return VisiblePosition();
1744 Node* node = m_renderer->node();
1746 return VisiblePosition();
1749 return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
1751 Position start, end;
1752 bool selected = Range::selectNodeContents(node, start, end);
1754 return VisiblePosition();
1756 CharacterIterator it(start, end);
1757 it.advance(index - 1);
1758 return VisiblePosition(Position(it.endContainer(), it.endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM);
1761 int AXRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
1763 if (isNativeTextControl() && m_renderer->isTextControl()) {
1764 HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1765 return textControl->indexForVisiblePosition(pos);
1768 if (!isTextControl())
1771 Node* node = m_renderer->node();
1775 Position indexPosition = pos.deepEquivalent();
1776 if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
1779 RefPtrWillBeRawPtr<Range> range = Range::create(m_renderer->document());
1780 range->setStart(node, 0, IGNORE_EXCEPTION);
1781 range->setEnd(indexPosition, IGNORE_EXCEPTION);
1783 return TextIterator::rangeLength(range.get());
1786 void AXRenderObject::addInlineTextBoxChildren()
1788 Settings* settings = document()->settings();
1789 if (!settings || !settings->inlineTextBoxAccessibilityEnabled())
1792 if (!renderer() || !renderer()->isText())
1795 if (renderer()->needsLayout()) {
1796 // If a RenderText needs layout, its inline text boxes are either
1797 // nonexistent or invalid, so defer until the layout happens and
1798 // the renderer calls AXObjectCache::inlineTextBoxesUpdated.
1802 RenderText* renderText = toRenderText(renderer());
1803 for (RefPtr<AbstractInlineTextBox> box = renderText->firstAbstractInlineTextBox(); box.get(); box = box->nextInlineTextBox()) {
1804 AXObject* axObject = axObjectCache()->getOrCreate(box.get());
1805 if (!axObject->accessibilityIsIgnored())
1806 m_children.append(axObject);
1810 void AXRenderObject::lineBreaks(Vector<int>& lineBreaks) const
1812 if (!isTextControl())
1815 VisiblePosition visiblePos = visiblePositionForIndex(0);
1816 VisiblePosition savedVisiblePos = visiblePos;
1817 visiblePos = nextLinePosition(visiblePos, 0);
1818 while (!visiblePos.isNull() && visiblePos != savedVisiblePos) {
1819 lineBreaks.append(indexForVisiblePosition(visiblePos));
1820 savedVisiblePos = visiblePos;
1821 visiblePos = nextLinePosition(visiblePos, 0);
1829 bool AXRenderObject::isAllowedChildOfTree() const
1831 // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1832 AXObject* axObj = parentObject();
1833 bool isInTree = false;
1835 if (axObj->isTree()) {
1839 axObj = axObj->parentObject();
1842 // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1844 AccessibilityRole role = roleValue();
1845 if (role != TreeItemRole && role != StaticTextRole)
1851 void AXRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
1853 bool isMulti = isMultiSelectable();
1855 AccessibilityChildrenVector childObjects = children();
1856 unsigned childrenSize = childObjects.size();
1857 for (unsigned k = 0; k < childrenSize; ++k) {
1858 // Every child should have aria-role option, and if so, check for selected attribute/state.
1859 AXObject* child = childObjects[k].get();
1860 if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) {
1861 result.append(child);
1868 AXObject::PlainTextRange AXRenderObject::ariaSelectedTextRange() const
1870 Node* node = m_renderer->node();
1872 return PlainTextRange();
1874 VisibleSelection visibleSelection = selection();
1875 RefPtrWillBeRawPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
1876 if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION))
1877 return PlainTextRange();
1879 int start = indexForVisiblePosition(visibleSelection.visibleStart());
1880 int end = indexForVisiblePosition(visibleSelection.visibleEnd());
1882 return PlainTextRange(start, end - start);
1885 bool AXRenderObject::nodeIsTextControl(const Node* node) const
1890 const AXObject* axObjectForNode = axObjectCache()->getOrCreate(const_cast<Node*>(node));
1891 if (!axObjectForNode)
1894 return axObjectForNode->isTextControl();
1897 bool AXRenderObject::isTabItemSelected() const
1899 if (!isTabItem() || !m_renderer)
1902 Node* node = m_renderer->node();
1903 if (!node || !node->isElementNode())
1906 // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
1907 // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
1908 // focus inside of it.
1909 AXObject* focusedElement = focusedUIElement();
1910 if (!focusedElement)
1913 WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
1914 elementsFromAttribute(elements, aria_controlsAttr);
1916 unsigned count = elements.size();
1917 for (unsigned k = 0; k < count; ++k) {
1918 Element* element = elements[k];
1919 AXObject* tabPanel = axObjectCache()->getOrCreate(element);
1921 // A tab item should only control tab panels.
1922 if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
1925 AXObject* checkFocusElement = focusedElement;
1926 // Check if the focused element is a descendant of the element controlled by the tab item.
1927 while (checkFocusElement) {
1928 if (tabPanel == checkFocusElement)
1930 checkFocusElement = checkFocusElement->parentObject();
1937 AXObject* AXRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
1942 AXObject* parent = axObjectCache()->getOrCreate(area->imageElement());
1946 AXObject::AccessibilityChildrenVector children = parent->children();
1947 unsigned count = children.size();
1948 for (unsigned k = 0; k < count; ++k) {
1949 if (children[k]->elementRect().contains(point))
1950 return children[k].get();
1956 bool AXRenderObject::renderObjectIsObservable(RenderObject* renderer) const
1958 // AX clients will listen for AXValueChange on a text control.
1959 if (renderer->isTextControl())
1962 // AX clients will listen for AXSelectedChildrenChanged on listboxes.
1963 Node* node = renderer->node();
1964 if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox()))
1967 // Textboxes should send out notifications.
1968 if (nodeHasRole(node, "textbox"))
1974 RenderObject* AXRenderObject::renderParentObject() const
1979 RenderObject* startOfConts = m_renderer->isRenderBlock() ? startOfContinuations(m_renderer) : 0;
1981 // Case 1: node is a block and is an inline's continuation. Parent
1982 // is the start of the continuation chain.
1983 return startOfConts;
1986 RenderObject* parent = m_renderer->parent();
1987 startOfConts = parent && parent->isRenderInline() ? startOfContinuations(parent) : 0;
1989 // Case 2: node's parent is an inline which is some node's continuation; parent is
1990 // the earliest node in the continuation chain.
1991 return startOfConts;
1994 RenderObject* firstChild = parent ? parent->slowFirstChild() : 0;
1995 if (firstChild && firstChild->node()) {
1996 // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
1997 // Get the node's renderer and follow that continuation chain until the first child is found.
1998 for (RenderObject* nodeRenderFirstChild = firstChild->node()->renderer(); nodeRenderFirstChild != firstChild; nodeRenderFirstChild = firstChild->node()->renderer()) {
1999 for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) {
2000 if (contsTest == firstChild) {
2001 parent = nodeRenderFirstChild->parent();
2005 RenderObject* newFirstChild = parent->slowFirstChild();
2006 if (firstChild == newFirstChild)
2008 firstChild = newFirstChild;
2009 if (!firstChild->node())
2017 bool AXRenderObject::isDescendantOfElementType(const HTMLQualifiedName& tagName) const
2019 for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
2020 if (parent->node() && parent->node()->hasTagName(tagName))
2026 bool AXRenderObject::isSVGImage() const
2028 return remoteSVGRootElement();
2031 void AXRenderObject::detachRemoteSVGRoot()
2033 if (AXSVGRoot* root = remoteSVGRootElement())
2037 AXSVGRoot* AXRenderObject::remoteSVGRootElement() const
2039 if (!m_renderer || !m_renderer->isRenderImage())
2042 ImageResource* cachedImage = toRenderImage(m_renderer)->cachedImage();
2046 Image* image = cachedImage->image();
2047 if (!image || !image->isSVGImage())
2050 FrameView* frameView = toSVGImage(image)->frameView();
2053 Document* doc = frameView->frame().document();
2054 if (!doc || !doc->isSVGDocument())
2057 Settings* settings = doc->settings();
2058 if (settings && !settings->accessibilityEnabled())
2059 settings->setAccessibilityEnabled(true);
2061 SVGSVGElement* rootElement = doc->accessSVGExtensions().rootElement();
2064 RenderObject* rendererRoot = rootElement->renderer();
2068 AXObject* rootSVGObject = doc->axObjectCache()->getOrCreate(rendererRoot);
2070 // In order to connect the AX hierarchy from the SVG root element from the loaded resource
2071 // the parent must be set, because there's no other way to get back to who created the image.
2072 ASSERT(rootSVGObject && rootSVGObject->isAXSVGRoot());
2073 if (!rootSVGObject->isAXSVGRoot())
2076 return toAXSVGRoot(rootSVGObject);
2079 AXObject* AXRenderObject::remoteSVGElementHitTest(const IntPoint& point) const
2081 AXObject* remote = remoteSVGRootElement();
2085 IntSize offset = point - roundedIntPoint(elementRect().location());
2086 return remote->accessibilityHitTest(IntPoint(offset));
2089 // The boundingBox for elements within the remote SVG element needs to be offset by its position
2090 // within the parent page, otherwise they are in relative coordinates only.
2091 void AXRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const
2093 for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
2094 if (parent->isAXSVGRoot()) {
2095 rect.moveBy(parent->parentObject()->elementRect().location());
2101 // Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
2102 // meaning that they should be exposed to the AX hierarchy.
2103 void AXRenderObject::addHiddenChildren()
2105 Node* node = this->node();
2109 // First do a quick run through to determine if we have any hidden nodes (most often we will not).
2110 // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
2111 bool shouldInsertHiddenNodes = false;
2112 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
2113 if (!child->renderer() && isNodeAriaVisible(child)) {
2114 shouldInsertHiddenNodes = true;
2119 if (!shouldInsertHiddenNodes)
2122 // Iterate through all of the children, including those that may have already been added, and
2123 // try to insert hidden nodes in the correct place in the DOM order.
2124 unsigned insertionIndex = 0;
2125 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
2126 if (child->renderer()) {
2127 // Find out where the last render sibling is located within m_children.
2128 AXObject* childObject = axObjectCache()->get(child->renderer());
2129 if (childObject && childObject->accessibilityIsIgnored()) {
2130 AccessibilityChildrenVector children = childObject->children();
2131 if (children.size())
2132 childObject = children.last().get();
2138 insertionIndex = m_children.find(childObject) + 1;
2142 if (!isNodeAriaVisible(child))
2145 unsigned previousSize = m_children.size();
2146 if (insertionIndex > previousSize)
2147 insertionIndex = previousSize;
2149 insertChild(axObjectCache()->getOrCreate(child), insertionIndex);
2150 insertionIndex += (m_children.size() - previousSize);
2154 void AXRenderObject::addTextFieldChildren()
2156 Node* node = this->node();
2157 if (!isHTMLInputElement(node))
2160 HTMLInputElement& input = toHTMLInputElement(*node);
2161 Element* spinButtonElement = input.userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton());
2162 if (!spinButtonElement || !spinButtonElement->isSpinButtonElement())
2165 AXSpinButton* axSpinButton = toAXSpinButton(axObjectCache()->getOrCreate(SpinButtonRole));
2166 axSpinButton->setSpinButtonElement(toSpinButtonElement(spinButtonElement));
2167 axSpinButton->setParent(this);
2168 m_children.append(axSpinButton);
2171 void AXRenderObject::addImageMapChildren()
2173 RenderBoxModelObject* cssBox = renderBoxModelObject();
2174 if (!cssBox || !cssBox->isRenderImage())
2177 HTMLMapElement* map = toRenderImage(cssBox)->imageMap();
2181 for (HTMLAreaElement* area = Traversal<HTMLAreaElement>::firstWithin(*map); area; area = Traversal<HTMLAreaElement>::next(*area, map)) {
2182 // add an <area> element for this child if it has a link
2183 if (area->isLink()) {
2184 AXImageMapLink* areaObject = toAXImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole));
2185 areaObject->setHTMLAreaElement(area);
2186 areaObject->setHTMLMapElement(map);
2187 areaObject->setParent(this);
2188 if (!areaObject->accessibilityIsIgnored())
2189 m_children.append(areaObject);
2191 axObjectCache()->remove(areaObject->axObjectID());
2196 void AXRenderObject::addCanvasChildren()
2198 if (!isHTMLCanvasElement(node()))
2201 // If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
2202 // Clear m_haveChildren because AXNodeObject::addChildren will expect it to be false.
2203 ASSERT(!m_children.size());
2204 m_haveChildren = false;
2205 AXNodeObject::addChildren();
2208 void AXRenderObject::addAttachmentChildren()
2210 if (!isAttachment())
2213 // FrameView's need to be inserted into the AX hierarchy when encountered.
2214 Widget* widget = widgetForAttachmentView();
2215 if (!widget || !widget->isFrameView())
2218 AXObject* axWidget = axObjectCache()->getOrCreate(widget);
2219 if (!axWidget->accessibilityIsIgnored())
2220 m_children.append(axWidget);
2223 void AXRenderObject::addPopupChildren()
2225 if (!isHTMLInputElement(node()))
2227 if (AXObject* axPopup = toHTMLInputElement(node())->popupRootAXObject())
2228 m_children.append(axPopup);
2231 void AXRenderObject::addRemoteSVGChildren()
2233 AXSVGRoot* root = remoteSVGRootElement();
2237 root->setParent(this);
2239 if (root->accessibilityIsIgnored()) {
2240 AccessibilityChildrenVector children = root->children();
2241 unsigned length = children.size();
2242 for (unsigned i = 0; i < length; ++i)
2243 m_children.append(children[i]);
2245 m_children.append(root);
2249 void AXRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
2251 // Get all the rows.
2252 AccessibilityChildrenVector allRows;
2254 ariaTreeRows(allRows);
2255 else if (isAXTable() && toAXTable(this)->supportsSelectedRows())
2256 allRows = toAXTable(this)->rows();
2258 // Determine which rows are selected.
2259 bool isMulti = isMultiSelectable();
2261 // Prefer active descendant over aria-selected.
2262 AXObject* activeDesc = activeDescendant();
2263 if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
2264 result.append(activeDesc);
2269 unsigned count = allRows.size();
2270 for (unsigned k = 0; k < count; ++k) {
2271 if (allRows[k]->isSelected()) {
2272 result.append(allRows[k]);
2279 bool AXRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
2284 return equalIgnoringCase(getAttribute(attributeName), "true");
2287 bool AXRenderObject::inheritsPresentationalRole() const
2289 // ARIA states if an item can get focus, it should not be presentational.
2290 if (canSetFocusAttribute())
2293 // ARIA spec says that when a parent object is presentational, and it has required child elements,
2294 // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
2295 // http://www.w3.org/WAI/PF/aria/complete#presentation
2296 if (roleValue() != ListItemRole && roleValue() != ListMarkerRole)
2299 AXObject* parent = parentObject();
2300 if (!parent->isAXRenderObject())
2303 Node* elementNode = toAXRenderObject(parent)->node();
2304 if (!elementNode || !elementNode->isElementNode())
2307 QualifiedName tagName = toElement(elementNode)->tagQName();
2308 if (tagName == ulTag || tagName == olTag || tagName == dlTag)
2309 return (parent->roleValue() == NoneRole || parent->roleValue() == PresentationalRole);
2314 LayoutRect AXRenderObject::computeElementRect() const
2316 RenderObject* obj = m_renderer;
2319 return LayoutRect();
2321 if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
2322 obj = obj->node()->renderer();
2324 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
2325 // For a web area, which will have the most elements of any element, absoluteQuads should be used.
2326 // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied.
2327 Vector<FloatQuad> quads;
2330 toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
2331 else if (isWebArea() || obj->isSVGRoot())
2332 obj->absoluteQuads(quads);
2334 obj->absoluteFocusRingQuads(quads);
2336 LayoutRect result = boundingBoxForQuads(obj, quads);
2338 Document* document = this->document();
2339 if (document && document->isSVGDocument())
2340 offsetBoundingBoxForRemoteSVGElement(result);
2341 if (document && document->frame() && document->frame()->pagePopupOwner()) {
2342 IntPoint popupOrigin = document->view()->contentsToScreen(IntRect()).location();
2343 IntPoint mainOrigin = axObjectCache()->rootObject()->documentFrameView()->contentsToScreen(IntRect()).location();
2344 result.moveBy(IntPoint(popupOrigin - mainOrigin));
2347 // The size of the web area should be the content size, not the clipped size.
2348 if (isWebArea() && obj->frame()->view())
2349 result.setSize(obj->frame()->view()->contentsSize());
2351 // Checkboxes and radio buttons include their label as part of their rect.
2352 if (isCheckboxOrRadio()) {
2353 HTMLLabelElement* label = labelForElement(toElement(m_renderer->node()));
2354 if (label && label->renderer()) {
2355 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect();
2356 result.unite(labelRect);
2363 } // namespace blink